在 C 中,将两个不连续的内存块作为一个连续的块访问?

Accessing two discontinuous memory blocks as a single continuous block, in C?

在Linux用户space:假设我分配了一个3MiB的连续内存ABC,其中ABC每个都是 1MiB。有没有办法以某种方式访问​​ AC 作为单个连续的 2MiB 内存(一种用户 space MMU)?

背景:我的场景是用C语言对一个ASIC芯片进行建模。该芯片有3MiB内存,芯片中的两个组件可以访问相同的内存硬件,但具有不同的地址映射。要在 C 中对其进行建模,一种方法是为两个组件复制 3MiB 内存,并在两个内存副本之间添加一些同步机制。另一种方法可能只是拥有 3MiB 内存的单个副本,一个组件按原样访问内存,而对于另一个组件,添加一种包装器来进行地址转换。我只想知道是否有更简洁的方法,避免内存重复和地址转换包装器...

PS: 根据Maxim的回答,我写了一个简单的测试程序,运行正常。看来我不应该为了我的目的使用 MAP_ANONYMOUSE 标志。此外,根据 mmap() 手册,为了 MAP_FIXED 正常工作,C 的对齐方式应该是目标平台上 PAGE_SIZE 的倍数(我的 x86_64 Ubuntu).我粘贴下面的代码以防万一...

#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>

#define SZ_A     (64 * 1024)   // A
#define SZ_B    (204 * 1024)   // B
#define SZ_C      (4 * 1024)   // C
#define SZ_ABC  (272 * 1024)   // ABC
#define SZ_AB   (268 * 1024)   // AB
#define SZ_AC    (68 * 1024)   // AC

const char shm_name[] = "/shm_name1";
unsigned char* p_abc = NULL;
unsigned char* p_ac = NULL;

#define MMAP_PROT  (PROT_READ | PROT_WRITE)

#define USE_MAP_ANONYMOUSE  0
#if USE_MAP_ANONYMOUSE
#define MMAP_FLAG        (MAP_SHARED | MAP_ANONYMOUS)
#define MMAP_FLAG_FIXED  (MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED)
#else
#define MMAP_FLAG        (MAP_SHARED)
#define MMAP_FLAG_FIXED  (MAP_SHARED | MAP_FIXED)
#endif

int main(void)
{
    int fd = - 1;
    void* p = NULL;
    fd = shm_open(shm_name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
    if (fd == - 1) {
        printf("shm_open() fail\n");
        return - 1;
    }

    if (0 != ftruncate(fd, SZ_ABC)) {
        printf("ftruncate() fail\n");
        return - 1;
    }

    if (0 != shm_unlink(shm_name)) {
        printf("shm_unlink() fail\n");
        return - 1;
    }

    p_abc = (unsigned char*)mmap(NULL, SZ_ABC, MMAP_PROT, MMAP_FLAG, fd, 0);
    if (p_abc == (unsigned char*) -1) {
        printf("p_abc = mmap(NULL) fail\n");
        p_abc = NULL;
        goto EXIT;
    }

    p_ac = (unsigned char*)mmap(NULL, SZ_AC, MMAP_PROT, MMAP_FLAG, fd, 0);
    if (p_ac == (unsigned char*) -1) {
        printf("p_ac = mmap(NULL) fail\n");
        p_ac = NULL;
        goto EXIT;
    }

    p = mmap(p_ac + SZ_A, SZ_C, MMAP_PROT, MMAP_FLAG_FIXED, fd, SZ_AB);
    if (p == MAP_FAILED || p != (void*)(p_ac + SZ_A)) {
        printf("mmap(MAP_FIXED) fail\n");
        p = NULL;
        goto EXIT;
    }
    close(fd);

    printf("mmap() ok:"
            "\np_abc=0x%" PRIxPTR
            "\n p_ac=0x%" PRIxPTR
            "\n    p=0x%" PRIxPTR "\n",
            (uintptr_t)p_abc, (uintptr_t)p_ac, (uintptr_t)p);

    // test
    memset(p_abc, 0xab, SZ_AB);
    memset(p_abc + SZ_AB, 0x0c, SZ_C);
    // should be: ab, 0c
    printf("sm4: %02x, %02x\n", p_ac[0], p_ac[SZ_A]);

    memset(p_ac, 0x0c, SZ_AC);
    // should be: 0c, 0c
    printf("sm0: %02x, %02x\n", p_abc[0], p_abc[SZ_AB]);

EXIT:
    if (p) munmap(p, SZ_C);
    if (p_ac) munmap(p_ac, SZ_AC);
    if (p_abc) munmap(p_abc, SZ_ABC);

    return 0;
}


suppose that I allocated a 3MiB continuous memory ABC, where A, B and C are 1MiB each. Is there a way to access AC as a single continuous 2MiB memory somehow (a kind of user space MMU)?

您可以使用 shm_open 创建一个 3MiB 大小的内存映射文件(并且,可选地,立即 shm_unlink 它)。然后使用不同的偏移量和大小多次将该文件映射到进程地址 space。

请注意,要连续映射 AC,您首先需要 mmap 2MiB 的匿名内存来保留一个连续的地址块 space。然后 mmap 前半部分 A 和后半部分 C 使用 MAP_FIXED 标志。

伪代码:

// Create an anonymous shared memory file.
int fd = shm_open(filename);
shm_unlink(filename);
ftruncate(fd, 3MiB);

// Map A and C contiguously.
void* ac = mmap(NULL, 2MiB, ..., fd, 0MiB); // maps AB part of ABC.
mmap(ac + 1MiB, 1MiB, ... | MAP_FIXED, fd, 2MiB); // remaps C part over B part (B gets unmapped).