使用 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);
}
我的问题:
我必须遍历指针数组以映射数组中的每个后续地址,还是我真的只需要从第一个映射返回的指针?
如果不需要,是否建议还是这样做?
是否有任何实际理由始终将 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内有效。
回答您的问题:
- 您可以一次性调用
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()
为父进程和子进程初始化共享内存 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);
}
我的问题:
我必须遍历指针数组以映射数组中的每个后续地址,还是我真的只需要从第一个映射返回的指针?
如果不需要,是否建议还是这样做?
是否有任何实际理由始终将
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内有效。
回答您的问题:
- 您可以一次性调用
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);
}