未调用shmctl(IPC_RMID),Linux下System V共享内存随写进程退出消失
问题:System V共享内存随进程退出自动消失的原因
使用System V共享内存时,写进程通过shmget()成功创建共享内存段,未调用带IPC_RMID参数的shmctl()删除它,但写进程结束后共享内存段直接消失,读进程无法访问。该程序在macOS上运行正常(共享内存会保留),但在Ubuntu Linux 5.15.0-127-generic上出现此异常。
写进程代码(writer.c)
#include <stdio.h> #include <stdlib.h> #include <sys/ipc.h> #include <sys/shm.h> #include <unistd.h> #define SHM_KEY 0x1234 #define SHM_SIZE sizeof(int) int main() { int shmid = shmget(SHM_KEY, SHM_SIZE, IPC_CREAT | 0666); if (shmid == -1) { perror("shmget failed"); exit(1); } int *shm_ptr = (int *)shmat(shmid, NULL, 0); if (shm_ptr == (void *)-1) { perror("shmat failed"); exit(1); } *shm_ptr = 42; printf("Writer: wrote %d to shared memory.\n", *shm_ptr); // Keeping writer alive printf("Writer: sleeping for 30 seconds...\n"); sleep(30); shmdt(shm_ptr); return 0; }
读进程代码(reader.c)
#include <stdio.h> #include <stdlib.h> #include <sys/ipc.h> #include <sys/shm.h> #define SHM_KEY 0x1234 #define SHM_SIZE sizeof(int) int main() { int shmid = shmget(SHM_KEY, SHM_SIZE, 0666); if (shmid == -1) { perror("shmget failed"); exit(1); } int *shm_ptr = (int *)shmat(shmid, NULL, 0); if (shm_ptr == (void *)-1) { perror("shmat failed"); exit(1); } printf("Reader: read %d from shared memory.\n", *shm_ptr); shmdt(shm_ptr); return 0; }
原因分析与解决办法
1. 跨平台机制差异
- macOS的System V共享内存默认持久化:除非显式调用
shmctl(shmid, IPC_RMID, NULL)删除或系统重启,否则共享内存段会一直存在。 - Linux下,正常情况下,当共享内存段的连接计数(
nattch)归0且未标记IPC_RMID时,内核不会立即销毁它,但Ubuntu的系统配置可能触发自动清理。
2. 系统配置导致的自动清理
(1)kernel.shm_rmid_forced内核参数
该参数控制内核是否在最后一个进程断开共享内存连接时自动删除段:
- 执行
sysctl kernel.shm_rmid_forced查看当前值,若输出为kernel.shm_rmid_forced = 1,则会触发自动删除。 - 临时修改:
sudo sysctl -w kernel.shm_rmid_forced=0 - 永久生效:编辑
/etc/sysctl.conf,添加kernel.shm_rmid_forced=0,然后执行sudo sysctl -p加载配置。
(2)systemd-tmpfiles自动清理规则
Ubuntu的systemd-tmpfiles-setup.service会按配置清理临时IPC资源:
- 检查配置文件
/usr/lib/tmpfiles.d/ipc.conf或/etc/tmpfiles.d/ipc.conf,若存在针对/dev/shm/*或System V共享内存的清理规则,会在进程退出后自动删除无连接的共享内存段。 - 解决:注释掉对应清理规则,然后执行
sudo systemctl restart systemd-tmpfiles-setup.service。
3. 通用解决方案
- 显式管理生命周期:在需要保留共享内存时,确保有一个进程保持连接(比如守护进程);不再需要时,调用
shmctl(shmid, IPC_RMID, NULL)显式删除。 - 避免依赖系统默认行为:不同Linux发行版的IPC资源管理规则可能不同,显式控制是最可靠的方式。
内容的提问来源于stack exchange,提问作者StaY_Hungry
相关产品推荐
相关产品推荐

