shmget 返回 -1 且 errno=EINVAL 通常因 key 无效、size 未页面对齐或系统限制已满;需用 getpagesize() 对齐 size,ftok() 生成合法 key,并用 ipcs/ipcrm 检查调整系统参数。

shmget 返回 -1 且 errno=EINVAL 怎么办
这通常是因为 key 无效、size 不对齐,或系统限制已满。Linux 共享内存段大小必须是页面对齐的(通常是 4096 字节),传入非对齐值(比如 1000)会导致 shmget 失败。
检查方式:
int size = 1000;
int shmid = shmget(key, size, IPC_CREAT | 0666);
if (shmid == -1) {
perror("shmget");
// 输出类似:Invalid argument
}解决方法:- 用
getpagesize()获取页大小,向上对齐:size = ((size + getpagesize() - 1) / getpagesize()) * getpagesize(); - 确认
key是合法的 —— 推荐用ftok("/tmp", 'a')生成,避免硬编码非法值(如 0 或负数) - 检查系统限制:
ipcs -l查看最大段数、单段最大字节等;必要时用sysctl -w kernel.shmall=2097152调整(需 root)
shmat 后指针操作越界却没报错?
shmat 返回的是 void*,它只是把共享内存映射到当前进程地址空间,并不提供边界检查。写超 shmget 指定的 size,可能覆盖相邻内存、触发段错误,也可能静默破坏其他变量 —— 行为完全未定义。
务必自己维护长度信息:
- 不要依赖
sizeof(*ptr)判断共享区大小 - 建议封装结构体头 + 数据体,头里存实际有效长度(如
struct shm_header { size_t len; char data[]; }) - 使用前强制校验:
if (ptr == (void*)-1) { /* 错误 */ },但注意:成功返回也可能为NULL(若用了SHM_REMAP且地址冲突)
进程退出后共享内存还在,怎么清理
共享内存段生命周期独立于创建它的进程。shmdt 只解除映射,shmctl(shmid, IPC_RMID, nullptr) 才真正标记删除 —— 但要等所有进程都 shmdt 后才真正释放。
常见误操作:
- 只调
shmdt就以为清理完了 → 实际段仍存在,ipcs -m还能看见,且shmget会复用旧段 - 在子进程里调
IPC_RMID→ 父进程再访问会出错(shmat失败或读到垃圾) - 没做异常路径清理 → crash 或 early return 导致漏掉
shmctl
- 由创建者(通常是父进程或主控进程)统一负责
IPC_RMID - 用
atexit()或 RAII(如自定义shm_segment类)确保释放 - 调试时用
ipcs -m+ipcrm -M手动清理残留
多个进程如何安全读写同一块共享内存
共享内存本身不带同步机制。直接并发读写必然导致数据竞争,struct 成员被部分更新、计数器错乱、指针悬空都是常态。
必须配合其他 IPC 原语:
- 用
semget/semop配套信号量(推荐):初始化时semget(key, 1, IPC_CREAT|0666),读写前后semopP/V - 慎用
pthread_mutex_t:需放在共享内存内且用PTHREAD_PROCESS_SHARED属性初始化,否则只对本进程有效 - 避免轮询等待:不要用 while(!flag) sleep(1),改用信号量或
sem_wait - 注意初始化顺序:信号量必须在共享内存就绪后初始化,否则其他进程
semop会失败
// 初始化信号量(仅一次)
int semid = semget(ftok("/tmp", 's'), 1, IPC_CREAT|0666);
semctl(semid, 0, SETVAL, 1); // 初始值为 1
// 写前加锁
struct sembuf op = {0, -1, SEM_UNDO};
semop(semid, &op, 1);
// ... memcpy(shm_ptr, data, size) ...
// 写后解锁
op.sem_op = 1;
semop(semid, &op, 1);
共享内存不是“开箱即用”的安全通信方式,页对齐、显式销毁、外置同步这三点漏掉任何一项,上线后都容易变成偶发性崩溃或数据错乱。尤其在多进程长期运行的服务中,残留段和未释放信号量会越积越多。
立即学习“C++免费学习笔记(深入)”;











