您如何正确清理和重新使用 SysV 共享内存段?

How do you correctly cleanup and re-use SysV shared memory segments?

我一直在为使用 Linux SysV SHM 接口的 API 编写一些粗糙的测试代码。 即 ftok()、shmget()、shmctl()、shmat()

不幸的是,我不能随意更改此界面(例如使用 shm_open() 代替 或从 orbit).

我通过集成测试发现了一个错误,该错误使用共享内存生成了两个不相关的进程,因为它没有使用 shmdt() 清理共享内存段。我进一步发现 惯例是使用:

shmctl(segmentId, IPC_RMID, 0);

告诉系统一旦所有进程分离就可以删除该段。 将此添加到集成测试中修复了共享内存未清理的错误。

使用 ipcs -m 查看段,您可以看到它们现在被标记为“dest”。

我还创建了较低级别的 'unit' 测试,这些测试通过分叉子进程来创建共享内存来工作。 尝试对测试代码应用相同的更正我有一些奇怪的观察结果:

问 (sysV) SHM 在父进程和子进程上下文中的工作方式是否有什么特别之处,这意味着 IPC_RMID 行为不同或者不需要?

问在子进程中使用 IPC_RMID 一段时间后 'removed' 之后,是否有编程方式可以安全地重新使用段 ID?

问有没有办法在不重启的情况下强制删除(取消附加/删除)“ipcs -m”中标记为 dest 的段? (在 OS 级别)

问“ipcs -pm”显示的是实际的 pid 还是其他? 有没有另一种方法可以找出系统认为真正仍然附加的东西? (我相信什么都不是,因为我对所有使用 SHM 接口的东西都使用了 kill -9)

问有没有办法像调用 shmdt() 一样强制分离进程?

我被误导认为只要指定为所有者的进程附加到共享内存就调用 shmctl(segmentId, IPC_RMID) 是正确的形式。

事实上 IPC_RMID 在 所有 进程都已连接之前不应调用。

部分答案在这里:

https://comp.unix.programmer.narkive.com/iLg3PhfZ/shmctl-ipc-rmid-oddity

似乎 IPC_RMID 将该段设置为私有,因此没有新进程可以附加到它。

保证唯一段的一种方法是故意使用 IPC_PRIVATE 开头:

id = shmget(IPC_PRIVATE, IPC_CREAT | mode);

这也避免了使用 ftok() 的需要和与另一段发生冲突的风险。 不幸的是,我不能在这里使用它,因为接口是基于用 ftok() 识别段的。至少我理解这里的问题。

更聪明的人可能能够在重新使用之前用更好的清理方法来凑钱。

另见 https://www.linuxquestions.org/questions/programming-9/shmctl-ipc_rmid-precludes-further-attachments-574636/

同时考虑这个问题和答案: