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

Posted

技术标签:

【中文标题】使用 mmap() 为 2D 数组初始化共享内存,是不是还需要为后续指针映射内存?我应该改用 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() 为 2D 数组初始化共享内存,是否还需要为后续指针映射内存?我应该改用 shm 吗? 【发布时间】:2019-12-07 01:33:11 【问题描述】:

我正在使用mmap() 为父进程和子进程初始化共享内存空间,并且该对象恰好是双字符指针(即char**)。这是必要的,因为我将在子进程中将用户输入存储到此二维数组中,并且父进程将在子进程终止后访问相同的数据。在阅读了一些关于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);

我的问题:

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

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

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

【问题讨论】:

如果您mmap 有足够的内存,则无需为每个数组条目单独调用mmap。您可以将数组条目指向大 mmap 段的某个适当部分。 @jxh 这是非常正确的。我想我没有考虑到这一点。所以就像一个连续的一维数组和一点额外的数学? 没错。 【参考方案1】:

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

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

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

如果您想一次性完成所有分配,您可以调用malloc(N * (sizeof(char *) + M * sizeof(char))),也就是说,您可以为 N 个大小为 M 的 char 数组加上一个 char * 数组分配足够的内存。

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

其次,这是非常重要的

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

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

回答您的问题:

    您可以一次性调用mmap(),正如我在上面使用malloc() 显示的那样,或者循环调用。但是,如果您希望能够将其视为矩阵,则必须在循环中初始化指针。但是,不要做任何这些 - 见下文 建议取决于您是否经常需要新的历史记录条目,以及MAX_HISTORY 的大小。如果它很大并且不经常使用,请一个接一个分配,不要预先分配所有内容。如果经常使用或者数量少,请一次性分配。 人们在共享内存时使用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);

【讨论】:

反应很好。清除很多。谢谢!

以上是关于使用 mmap() 为 2D 数组初始化共享内存,是不是还需要为后续指针映射内存?我应该改用 shm 吗?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以使用 IPC 分配 2D 数组作为共享内存?

内存映射mmap 和 共享内存

linux共享内存和mmap的区别

共享内存之——mmap内存映射

Linux 共享内存:shmget() 与 mmap()?

通过使用 mmap() 在进程之间共享内存