使用 mmap() 为二维数组初始化共享内存,是否还需要为后续指针映射内存?我应该改用 shm 吗?

Initializing shared memory using mmap() for a 2D array, is it necessary to also map memory for subsequent pointers? Should I use shm instead?

我正在使用 mmap() 为父进程和子进程初始化共享内存 space,对象恰好是一个双字符指针(即 char**)。这是必要的,因为我将在子进程中将用户输入存储到此 2D 数组,并且父进程将在子进程终止后访问相同的数据。在阅读了一些关于 mmap() 的文档后,解决这个问题并不难,感觉很像 malloc(),但没有那么多承诺。

考虑下面的代码,直接从我正在编写的程序中复制:

/* mmap prot and flags */
int protection = PROT_READ | PROT_WRITE;     // readable and writable
int map_flags  = MAP_SHARED | MAP_ANONYMOUS; // no file, changes are propagated

/* initialize shared memory */                 
char **histv = (char**)mmap(NULL,              // (void*)  address
                (sizeof(char*) * MAX_HISTORY), // (size_t) length
                protection,                    // (int)    memory protection
                map_flags,                     // (int)    mapping flags
                -1,                            // (int)    file descriptor
                0);                            // (off_t)  addr offset

for (int i = 0; i < MAX_HISTORY; i++) {
    histv[i] = (char*)mmap(NULL,  
                (sizeof(char) * MAX_LINE), 
                protection,
                map_flags, 
                -1, 
                0);
}

我的问题:

  1. 我必须遍历指针数组以映射数组中的每个后续地址,还是我真的只需要从第一个映射返回的指针?

  2. 如果不需要,是否建议还是这样做?

  3. 是否有任何实际理由始终将 shm_open()ftruncate()mmap() 结合使用,而不是使用 MAP_ANONYMOUS? (注意:我从来没有用过前两者,我最近才读了很多关于共享内存的文章。)

首先,char a[N][M]char **a = malloc(...)是有区别的。

对于前者,您可以像在任何其他语言中一样将其视为矩阵。

对于后者,您将调用 malloc(N * sizeof(char *)),即您将分配 N 个指针,并且对于每个这样的指针,您将调用 malloc(M * sizeof(char)).

如果你想一次完成所有分配,你会调用 malloc(N * (sizeof(char *) + M * sizeof(char))),即你会为 N char 个大小为 M 的数组分配足够的内存,再加上一个 char *数组。

所有这些都适用于任何形式的内存分配,无论是 malloc(3)mmap(2) 还是其他 - 但是,请参阅以下内容。

其次,这是非常重要:

假设mmap()的目的是与其他进程共享内存,你不能将指针放在映射内存中

来自一个地址space(=进程)的指针仅在该地址space内有效。

回答您的问题:

  1. 您可以一次性调用 mmap(),就像我在上面用 malloc() 展示的那样,或者在循环中调用。但是,如果您希望能够将其视为矩阵,则必须在循环中初始化指针。但是,不要做任何这些 - 见下文
  2. 建议取决于您经常需要一个新的历史条目,以及有多大 MAX_HISTORY。如果它很大而且不经常使用,一个一个分配,不要预先分配所有东西。经常用或者小的就一次性分配。
  3. 人们在共享内存时使用 shm_open(3) 的原因是能够为内存附加一个名称,允许其他进程仅通过名称访问它。 ftruncate(2) 通常用于设置文件大小,因为如果文件小于您正在映射的大小,mmap() 将不会映射您想要的所有内存。

假设历史足够小,因为它可能是,你的案例要求如下:

// allocate a matrix in one go
char *histv = (char *)mmap(NULL, // note the type, char * not char **
                (sizeof(char*) * MAX_HISTORY * MAX_LINE), // note the size, no extra array
                protection,
                map_flags,
                -1,
                0);

// manually implement the same logic the C compiler implements for arrays such as a[N][M]
char *history_get(n)
{
    return histv + (n * MAX_LINE);
}