c++如何操作共享内存IPC_c++ shmget与shmat在Linux下的使用【方法】

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),读写前后 semop P/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);

共享内存不是“开箱即用”的安全通信方式,页对齐、显式销毁、外置同步这三点漏掉任何一项,上线后都容易变成偶发性崩溃或数据错乱。尤其在多进程长期运行的服务中,残留段和未释放信号量会越积越多。