使用 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 吗?的主要内容,如果未能解决你的问题,请参考以下文章