您如何正确清理和重新使用 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' 测试,这些测试通过分叉子进程来创建共享内存来工作。
尝试对测试代码应用相同的更正我有一些奇怪的观察结果:
如果我做正确的事并添加对 shmctl(segmentId, IPC_RMID) 的调用,测试将停止工作。
如果我删除此类调用,测试将再次开始工作。
(现在我的解决方案就是不在单元测试代码中使用 IPC_RMID)
我不确定这是否是问题所在,但是,由于 ftok() 容易发生冲突,在上一个测试删除它后,相同的 ID 用于某些测试,因此进一步调查。
不过,并非每次失败都是如此。
如果我从 O_CREAT 更改为 O_CREAT|O_EXCL 打开重新使用相同的令牌将失败并出现 EEXIST。
(建议 'dest' 段在某种意义上仍然存在并且可以重复使用)
我还注意到,从 运行 许多测试中,我有相当数量的 shm 段被列为“dest”,但 nattach=2 意味着两个进程未能调用 shmdt()。如果我列出了据称创建它并最后使用 ipcs -mp 使用它的 pid,它们不是创建或使用它的进程。相反,它们属于 firefox 之类的东西,这有点令人困惑!
我不明白这一点。大概它们会在重新启动时消失,但它们似乎与此问题没有直接关系。
我认为在我修复丢失的 shmdt() 和 shmctl(IPC_RMID) 问题之前,这些已经杀死了早期的集成测试。
ipcs -m 列出的许多段都有一个 0x00000000 的键,这看起来很可疑。
这些是私有段并不可疑。
问 (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() 识别段的。至少我理解这里的问题。
更聪明的人可能能够在重新使用之前用更好的清理方法来凑钱。
同时考虑这个问题和答案:
我一直在为使用 Linux SysV SHM 接口的 API 编写一些粗糙的测试代码。 即 ftok()、shmget()、shmctl()、shmat()
不幸的是,我不能随意更改此界面(例如使用 shm_open() 代替 或从 orbit).
我通过集成测试发现了一个错误,该错误使用共享内存生成了两个不相关的进程,因为它没有使用 shmdt() 清理共享内存段。我进一步发现 惯例是使用:
shmctl(segmentId, IPC_RMID, 0);
告诉系统一旦所有进程分离就可以删除该段。 将此添加到集成测试中修复了共享内存未清理的错误。
使用 ipcs -m 查看段,您可以看到它们现在被标记为“dest”。
我还创建了较低级别的 'unit' 测试,这些测试通过分叉子进程来创建共享内存来工作。 尝试对测试代码应用相同的更正我有一些奇怪的观察结果:
如果我做正确的事并添加对 shmctl(segmentId, IPC_RMID) 的调用,测试将停止工作。
如果我删除此类调用,测试将再次开始工作。 (现在我的解决方案就是不在单元测试代码中使用 IPC_RMID)
我不确定这是否是问题所在,但是,由于 ftok() 容易发生冲突,在上一个测试删除它后,相同的 ID 用于某些测试,因此进一步调查。 不过,并非每次失败都是如此。
如果我从 O_CREAT 更改为 O_CREAT|O_EXCL 打开重新使用相同的令牌将失败并出现 EEXIST。 (建议 'dest' 段在某种意义上仍然存在并且可以重复使用)
我还注意到,从 运行 许多测试中,我有相当数量的 shm 段被列为“dest”,但 nattach=2 意味着两个进程未能调用 shmdt()。如果我列出了据称创建它并最后使用 ipcs -mp 使用它的 pid,它们不是创建或使用它的进程。相反,它们属于 firefox 之类的东西,这有点令人困惑! 我不明白这一点。大概它们会在重新启动时消失,但它们似乎与此问题没有直接关系。 我认为在我修复丢失的 shmdt() 和 shmctl(IPC_RMID) 问题之前,这些已经杀死了早期的集成测试。
ipcs -m 列出的许多段都有一个 0x00000000 的键,这看起来很可疑。这些是私有段并不可疑。
问 (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() 识别段的。至少我理解这里的问题。
更聪明的人可能能够在重新使用之前用更好的清理方法来凑钱。
同时考虑这个问题和答案: