首页 > 解决方案 > Linux IPC:共享内存恢复

问题描述

我有两个进程(生产者和消费者)通过使用“旧”接口而不是 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 标志创建一个新区域。我注意到消费者可以继续从现有的共享内存段中读取,而替代生产者已经转移到另一个。

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

标签: linuxshared-memory

解决方案


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

  • 代替使用 IPC_CREAT,它可以首先检查是否存在可以重用的现有段。

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

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

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

您可以做几件事:

  1. 使用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;
     ...
  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 使用 inode 编号而不是手册页建议的实际文件名。所以你需要小心使用它:
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 是否实际上被您认为的“生产者”使用。

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

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

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

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

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

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

尽管文件存在并且与原始文件具有相同的 inode,但我发现 ftok() 会返回 -1 一段时间。


推荐阅读