Linux IPC:共享内存恢复

Linux IPC: shared memory recovery

我有两个进程(生产者和消费者)通过使用 'old' 接口而不是 mmap 生成的共享内存段进行通信:

auto key = ftok(<somefile>,<someid>;
int ret = shmget(key, size, flags);
void* memArea = shmat(key,NULL,0);
// check errors and do stuff...

生产者进程可能由于错误或配置更改而重新启动。 它每次使用 shmget() 的 IPC_CREAT 标志创建一个新区域。 我注意到消费者可以继续从现有的共享内存段读取,而替代生产者已经转移到另一个共享内存段。

消费者进程如何检测并从中恢复?

改变您的生产者设计可能是更好的主意:

  • 而不是使用 IPC_CREAT 它可以首先检查是否存在可以重新使用的现有段。

  • 您也可以考虑使用基于 mmap 的共享内存,这在某些方面更灵活。

  • 您可以使用一些其他指示器(例如锁定文件)来确定共享内存接口是否仍然可用。

但是,如果出于某种原因这些不是选项(例如其他人控制生产者代码),请继续阅读。

您可以做几件事:

  1. 使用shmctl()来'stat'你的内存段
 // 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;
     ...
  1. 检查该区域是否标记为删除(Linux 具体)
 if ((statBuf.shm_perm.mode&SHM_DEST) != 0) return false;
  1. 假设您附加在生产者之后并且它是创建者进程 - 检查它是否在您之后分离。 警告:如果您的设计允许,它可以重新连接。
 if (statBuf.shm_cpid == shmBuf.shm_lpid) return false;
  1. 检查创建进程的 PID 是一个 运行 进程。 警告:PID 可以被新进程回收
 if (getpgid(shmBuf.shm_cpid) == -1) return false;

注意:如果生产者不是其他用户,您可以使用 kill(shmBuf.shm_cpid,0)

  1. 您可能还想检查文件是否已被修改。 一个关键点是 ftok uses the inode number not the actual filename as the man page 建议。所以你需要小心使用它:
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 是否确实被您认为有用的 'producer' 使用。

  1. 清理陈旧的共享内存段

您现在可以自由地从段中分离 shmdt(),并尝试清理它 shmctl(shmid,IPC_RMID,NULL)。如果创建者未授予消费者进程可能没有删除它的权限。

  1. 附加到替换共享内存段

然后原则上您可以附加到替换生产者进程创建的任何新共享内存段:

auto key = ftok(<somefile>,<someid>;
void* memArea = shmat(key,NULL,0);
// check errors and do stuff...

但是等待你的是残酷而有趣的惩罚。它不会立即起作用。您必须等待一段时间并定期重试。我想这是在操作系统有机会清理旧内存段之前。

我发现 ftok() returns -1 一段时间,尽管该文件存在并且与原始文件具有相同的索引节点。