shmget 返回 -1 且 errno=EINVAL 通常因 key 无效、size 未页面对齐或系统限制已满;需用 getpagesize() 对齐 size,ftok() 生成合法 key,并用 ipcs/ipcrm 检查调整系统参数。
这通常是因为 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 返回的是 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 失败或读到垃圾)shmctl
IPC_RMID
atexit() 或 RAII(如自定义 shm_segment 类)确保释放ipcs -m + ipcrm -M 手动清理残留共享内存本身不带同步机制。直接并发读写必然导致数据竞争,struct 成员被部分更新、计数器错乱、指针悬空都是常态。
必须配合其他 IPC 原语:
semget/semop 配套信号量(推荐):初始化时 semget(key, 1, IPC_CREAT|0666),读写前后 semop P/Vpthread_mutex_t:需放在共享内存内且用
PTHREAD_PROCESS_SHARED 属性初始化,否则只对本进程有效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);
共享内存不是“开箱即用”的安全通信方式,页对齐、显式销毁、外置同步这三点漏掉任何一项,上线后都容易变成偶发性崩溃或数据错乱。尤其在多进程长期运行的服务中,残留段和未释放信号量会越积越多。