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 的共享内存,这在某些方面更灵活。
您可以使用一些其他指示器(例如锁定文件)来确定共享内存接口是否仍然可用。
但是,如果出于某种原因这些不是选项(例如其他人控制生产者代码),请继续阅读。
您可以做几件事:
- 使用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;
...
- 检查该区域是否标记为删除(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 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' 使用。
- 清理陈旧的共享内存段
您现在可以自由地从段中分离 shmdt(),并尝试清理它 shmctl(shmid,IPC_RMID,NULL)。如果创建者未授予消费者进程可能没有删除它的权限。
- 附加到替换共享内存段
然后原则上您可以附加到替换生产者进程创建的任何新共享内存段:
auto key = ftok(<somefile>,<someid>;
void* memArea = shmat(key,NULL,0);
// check errors and do stuff...
但是等待你的是残酷而有趣的惩罚。它不会立即起作用。您必须等待一段时间并定期重试。我想这是在操作系统有机会清理旧内存段之前。
我发现 ftok() returns -1 一段时间,尽管该文件存在并且与原始文件具有相同的索引节点。
我有两个进程(生产者和消费者)通过使用 '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 的共享内存,这在某些方面更灵活。
您可以使用一些其他指示器(例如锁定文件)来确定共享内存接口是否仍然可用。
但是,如果出于某种原因这些不是选项(例如其他人控制生产者代码),请继续阅读。
您可以做几件事:
- 使用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; ...
- 检查该区域是否标记为删除(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 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' 使用。
- 清理陈旧的共享内存段
您现在可以自由地从段中分离 shmdt(),并尝试清理它 shmctl(shmid,IPC_RMID,NULL)。如果创建者未授予消费者进程可能没有删除它的权限。
- 附加到替换共享内存段
然后原则上您可以附加到替换生产者进程创建的任何新共享内存段:
auto key = ftok(<somefile>,<someid>;
void* memArea = shmat(key,NULL,0);
// check errors and do stuff...
但是等待你的是残酷而有趣的惩罚。它不会立即起作用。您必须等待一段时间并定期重试。我想这是在操作系统有机会清理旧内存段之前。
我发现 ftok() returns -1 一段时间,尽管该文件存在并且与原始文件具有相同的索引节点。