共享内存进程间通信

Posted

技术标签:

【中文标题】共享内存进程间通信【英文标题】:Shared memory interprocess communication 【发布时间】:2011-11-29 16:44:58 【问题描述】:

我使用shmget分配了一块共享内存。

然后我使用shmat分别将共享内存附加到进程A和进程B。我假设shmat 返回的 A 和 B 的句柄(即 A 和 B 中映射到实际共享内存的地址)是不同的。

假设 A 和 B 的句柄分别定义为 char* pchar* q。 我的问题是,如果我们将一个对象写入进程 A 中的地址 p + sizeof(anotherObject),我们能否期望在进程 B 中的地址 q + sizeof(anotherObject) 处获得相同的对象?

我猜是这样,但不是 100% 确定。如果是这样,既然我们知道p + sizeof(anotherObject)q + sizeof(anotherObject) 指的是不同的内存位置,那么这种通信或映射机制是如何实现的?

【问题讨论】:

考虑使用 Boost.Interprocess - 有一个文档可以回答像你这样的问题。 考虑使用现代接口shm_openmmap 来完成这样的任务。它们限制较少,更容易处理。 【参考方案1】:

现代处理器使用虚拟地址空间。您在程序中使用的地址不是对象的“真实”地址,操作系统和处理器将内存页映射到地址范围,您将其用作“内存”。

创建共享内存仅仅意味着操作系统将相同的内存页面映射到两个或多个不同进程的地址空间。两个进程中的两个指针不具有相同数值这一事实没有任何意义,即使不同进程中的指针具有相同的数值,它们通常指向不同的内存位置。

关于将“对象”写入内存,首先要“序列化”它。我提到过不同进程中的指针是互不兼容的。如果你的对象有任何指针成员,或者其他可能包含指针的对象,你必须想出一种方法来用实际数据替换这些指针,因为一旦你从另一个线程读取对象,这些指针就和垃圾。

请注意,C++ std::sting、std::vector 和其他容器以及任何虚拟对象(具有虚拟功能的对象)在其中都有指针。所以如果你想传递一个字符串,你需要将它作为一个字符序列写入共享内存,并在另一端以相同的方式读取。

【讨论】:

【参考方案2】:

你能在共享内存区域中写出对象的二进制表示吗?是的。

您能否通过访问存储在共享内存中的二进制表示来安全地使用另一个程序中的对象?有时,但不是一般情况。

这些规则类似于将二进制表示形式写入磁盘或通过线路进行序列化。您必须确保处理指向其他对象以及所有非 POD 成员的指针。例如,std::string 实例有一个指向已分配内存的指针:

    可能不在共享内存区域中 可能在另一个进程中没有相同的地址(例如,数据在共享内存中,但共享内存区域在两个进程中映射到不同的基地址)。

所以,一个简单的启发式方法是,如果 X 是任何你不能的东西:

std::ofstream file("foo.bin",std::ios::binary);
file.write(reinterpret_cast<const char*>(&X), sizeof(X));

那么你就不能安全地把它写到共享内存中了。

注意事项:即使数据是POD类型,也不能假设写出来是安全的。在某些操作系统上,可以运行几种不同类型的进程(例如 32 位和 64 位进程)。在这种情况下,在两个进程中使用 int 之类的类型可能不同。给定一个特定的操作系统、一个特定的编译器和特定的编译设置,就有可能派生出一种可靠的方法来编写这种 POD 类型。如果这种环境可以改变,请避免像瘟疫一样这样做。

【讨论】:

您可以使用固定宽度的 POD,例如 int32_t 和 uint8_t,以确保 32 位和 64 位进程都可以使用它们。 @WTP:仍然不能保证两个进程都使用相同的编译器和相同的对齐方式。这比看起来要微妙得多。因此,实现这一点是可能,但不是保证

以上是关于共享内存进程间通信的主要内容,如果未能解决你的问题,请参考以下文章

进程间通信

进程间通信

进程间通信(共享内存)

Linux之进程间通信

Linux进程间通信

Linux-进程间通信