通过使用 mmap() 在进程之间共享内存
Posted
技术标签:
【中文标题】通过使用 mmap() 在进程之间共享内存【英文标题】:Sharing memory between processes through the use of mmap() 【发布时间】:2011-06-26 20:47:06 【问题描述】:我在 Linux 2.6 中。我有一个环境,其中 2 个进程通过消息传递模式的简单实现来模拟(使用共享内存)数据交换。
我有一个客户端进程(从父进程派生,即服务器),它将结构(消息)写入(在派生之后)创建的内存映射区域:
message *m = mmap(NULL, sizeof(message), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0)
这个指针然后被写入一个队列(以链表的形式)到另一个共享内存区域,这是服务器和客户端进程共有的(因为 if 是在 fork 之前使用上面相同的代码创建的)。然后服务器读取该区域,获取指向消息的指针并进行处理。
问题是 *m 是在 fork() 之后创建的,当服务器进程尝试访问指向的内存位置时,我得到一个分段错误。在客户端创建后,是否可以将该内存区域附加到服务器 POST 分叉?
注意:我不想在分叉之前将指针映射到消息(然后与服务器共享它),因为我通常不知道客户端想要发送到服务器的消息数量,以及那里可能有多个客户端进程,所以我想仅在客户端需要发送消息时创建一个新的共享内存块,并在服务器收到该消息后取消映射。
注意:这是出于学术目的:我知道这不是解决此问题的最佳方法,但我只需要遵循此路径。
提前致谢!
【问题讨论】:
【参考方案1】:是否可以在客户端创建后将该内存区域附加到服务器 POST 分叉?
MAP_ANONYMOUS|MAP_SHARED
映射内存只能由执行mmap()
调用的进程或其子进程访问。另一个进程无法映射相同的内存,因为它是匿名的,因此无法从其他地方引用该内存。
使用shm_open()
调用可以创建命名共享内存,这些共享内存可以被不相关的进程引用和映射。
【讨论】:
谢谢!我使用shm_open()
,然后使用mmap()
和给定的fd,它部分解决了问题。是否可以在映射 fd 后重命名(使用rename()
)我提供给shm_open()
的“标签”字符串?我尝试这样做,但是当我尝试使用重命名标签从另一个进程中使用shm_open()
时,我得到一个运行时“总线错误”。注意:我注意到在 /sys/shm/ 中存在重命名的文件,因此重命名似乎成功了。
shm_open("name")
在 Linux 上通常转换为 open("/dev/shm/name")
。您应该能够像普通文件一样重命名它。请注意,“/dev/shm/”是 Linux 上 tmpfs 的默认挂载点。它可以映射到其他地方。见google.com/codesearch/p?hl=en#xy1xtVWIKOQ/pub/glibc/releases/…
感谢您的来信。如果我尝试访问共享内存区域,您是否知道为什么重命名会导致总线错误?
shm_open()
是否会导致 SIGBUS
?
/dev/shm 或 shm_open 具有一半物理内存大小的默认配额限制。一开始你可以shm_open和mmap更大的内存空间,但是当你写超出限制或者别人也使用/dev/shm或者shm_open这样剩余空间为零时,你的程序直接获取SIGBUS。【参考方案2】:
那是行不通的。
如果你在 fork() 之后创建了一个映射,那么在其他相关进程中就不会一样了。
您不能假设以这种方式共享指针。
如果你真的想这样做(我不推荐它!),你应该在 fork() 之前映射一个大区域,然后以某种方式分配合适大小的缓冲区(没有与其他进程的竞争条件,当然!)并传递这些指针。
在 fork 之后调用 mmap() 的两个相关进程可能会返回相同的指针,指向不同的内存。事实上,这是极有可能的。
【讨论】:
【参考方案3】:仅适用于 2018 年及以后阅读此问题的任何人。 现在的解决方案是使用 memfd_create 创建一个匿名文件,并使用 unix 套接字将此文件句柄传递给其他进程。
memfd_create 是一个仅限 linux 的系统调用
【讨论】:
以上是关于通过使用 mmap() 在进程之间共享内存的主要内容,如果未能解决你的问题,请参考以下文章