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

27
我正在使用Linux 2.6。我有一个环境,其中有两个进程通过共享内存模拟数据交换,以简单的消息传递模式实现。
我有一个客户端进程(由父进程(即服务器)fork出),它将一个结构体(消息)写入到创建的内存映射区域中(在fork之后): message *m = mmap(NULL, sizeof(message), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0) 这个指针然后被写入到另一个共享内存区域(以链表形式)的队列中,该区域是服务器和客户端进程共用的(因为它是在fork之前使用相同的代码创建的)。然后服务器读取这个区域并获得指向消息的指针,对其进行处理。
问题是*m是在fork()之后创建的,当服务器进程尝试访问指向的内存位置时,会出现分段错误。是否可能在客户端创建它后,在POST forking时将那个内存区域附加到服务器?
注意:我不想在fork之前将指向消息的指针mmap(然后与服务器共享),因为我通常不知道客户端要发送给服务器多少条消息,并且可能会有多个客户端进程,因此我想在客户端需要发送消息时创建一个新的共享内存块,并在服务器接收到该消息后取消映射它。
注意:这是为了学术目的:我知道这不是解决此问题的最佳方法,但我只需要遵循这条路径。
提前感谢!
3个回答

45
在客户端创建内存区域后,是否有可能在服务器POST分叉之后将该内存区域附加到服务器上?
使用“MAP_ANONYMOUS | MAP_SHARED”映射的内存只能被执行“mmap()”调用或其子进程访问。无法通过其他进程映射相同的内存,因为该内存是匿名的,无法从其他位置引用。
使用“shm_open()”调用可以创建可以被不相关进程引用和映射的命名共享内存。

谢谢!我使用了shm_open(),然后使用给定的fd进行了mmap(),这在一定程度上解决了问题。在使用rename()重命名我提供给shm_open()的“tag”字符串之后,是否可以重新命名?我尝试过这样做,但是当我尝试使用重命名后的标签从另一个进程中使用shm_open()时,会出现运行时“总线错误”。注意:我注意到在/sys/shm/中,已经存在重命名的文件,因此似乎重命名成功了。 - Andrea Sprega
谢谢你的留言。你有没有任何线索,为什么重命名会导致总线错误,如果我尝试访问共享内存区域的话? - Andrea Sprega
1
shm_open() 会导致 SIGBUS 吗? - Maxim Egorushkin
是的。客户端进程执行shm_open(),mmap(),然后重命名“文件”。然后服务器再次使用shm_open()(带有重命名标签),我在终端中收到总线错误,我认为这对应于SIGBUS。 - Andrea Sprega
1
/dev/shm或shm_open具有默认的配额限制,为物理内存大小的一半。您可以在最初使用shm_open和mmap时打开更大的内存空间,但是当您超出限制写入或其他人也使用/dev/shm或shm_open以至于剩余空间为零时,您的程序将直接收到SIGBUS信号。 - jclin
显示剩余2条评论

29

针对2018年及以后的读者,解决方案是使用memfd_create创建一个匿名文件,并使用unix套接字将此文件句柄传递给其他进程。

memfd_create仅适用于Linux系统调用。


2
自2019年起,它不再是仅适用于Linux的系统调用了(参见https://reviews.freebsd.org/D21393),因为FreeBSD现在也有了它,尽管即使在此之前,您在FreeBSD上也可以访问等效功能,因为FreeBSD明确允许您对从shm_open(2)获得的描述符进行“ftruncate(2)”和“read(2)”。Linux实际上早在2014年的3.17版本中就已经有了它,而Glibc实际上直到2018年的2.27版本才公开了它。在OpenBSD≥5.4(2013)上,您可以使用“shm_mkstemp(2)”代替。另一个SO答案详细介绍了这些内容(参见https://dev59.com/q1MI5IYBdhLWcg3wpdG7#55711740)。 - Alex Shpilkin
@AlexShpilkin 不错,现在希望我们也能在 macOS 上得到它。不确定苹果公司现在在将 Darwin 与 FreeBSD 同步方面的表现如何。 - Lothar

6

这样做是行不通的。

如果在fork()之后创建映射,它在其他相关进程中将不会相同。

你不能以这种方式假设指针的共享。

如果你真的想以这种方式做(我不建议这样做!),你应该在fork()之前映射一个大区域,然后分配适当大小的缓冲区(当然要避免与其他进程发生竞争条件!)并传递这些指针。

两个相关的进程在fork()之后调用mmap(),可能会得到相同的指针,但指向不同的内存。事实上,这种情况非常普遍。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接