改变你的生产者设计可能是更好的主意:
-
它可以首先检查是否存在可以重用的现有段,而不是使用 IPC_CREAT。
-
您还可以考虑使用基于 mmap 的共享内存,这在某些方面更灵活。
-
您可以使用其他一些指标(例如锁定文件)来确定共享内存接口是否仍然可行。
但是,如果由于某种原因这些不是选项(例如其他人控制生产者代码),那么请继续阅读。
您可以执行以下几项操作:
- use shmctl https://linux.die.net/man/2/shmctl() 来“统计”你的内存段
// return true if the shared memory region is still 'useful/useable'
bool checkShm(int shmId)
{
struct shmid_ds statBuf;
int res = shmctl(<shmid>, IPC_STAT, statBuf);
if (res == -1) return false;
...
- 检查该区域是否被标记为删除(特定于 Linux)
if ((statBuf.shm_perm.mode&SHM_DEST) != 0) return false;
- 假设您在生产者之后附加,并且它是创建者进程 - 检查它是否在您之后分离。警告:如果您的设计允许,它可能会再次重新连接。
if (statBuf.shm_cpid == shmBuf.shm_lpid) return false;
- 检查创建者进程的 PID 是否是正在运行的进程。警告:PID 可以被新进程回收
if (getpgid(shmBuf.shm_cpid) == -1) return false;
注意:你可以使用kill(shmBuf.shm_cpid,0)
相反,如果生产者不是不同的用户。
- 您可能还想检查文件是否已被修改。
一个关键点是ftok 使用 inode 号 https://code.woboq.org/userspace/glibc/sysvipc/ftok.c.html不是实际的文件名man page https://linux.die.net/man/3/ftok建议。所以你需要小心使用它:
struct stat fstatBuf;
int res = stat(fileName,&fstatBuf);
if (res == -1) return false; // if the file has disappeared it could be a bad sign!
if (fstatBuf.st_ino != savedInode) return false;
完成所有这些后,您现在应该有一个相当好的方法来检查您认为仍然有用的 SHM 是否实际上被您认为的“生产者”使用。
- 清理陈旧的共享内存段
您现在可以自由地将 shmdt() 从段中分离出来,并尝试清理它 shmctl(shmid,IPC_RMID,NULL)。如果创建者未授予消费者进程可能无权删除它。
- 附加到替换共享内存段
然后,原则上您可以附加到由替换生产者进程创建的任何新共享内存段:
auto key = ftok(<somefile>,<someid>;
void* memArea = shmat(key,NULL,0);
// check errors and do stuff...
但残酷而有趣的惩罚正在等待着你。它不会立即起作用。您必须等待一段时间并定期重试。我想这是直到操作系统有机会清理旧的内存段为止。
我发现 ftok() 暂时返回 -1,尽管文件存在并且与原始文件具有相同的 inode。