调用shmat()返回invalid argument错误,共享内存读取异常排查
共享内存shmat报EINVAL(Invalid argument)的根因与修正
直接定位的代码错误(按影响优先级排序)
- 运算符优先级写错导致shmid赋值完全失效
C语言中关系运算符<的优先级远高于赋值运算符=,当前代码中if (shmid = shmget(key, MAX * sizeof(data),0666) < 0)的实际执行逻辑是:- 先执行shmget拿到合法的正整数共享内存id
- 将该id与0做布尔比较,得到结果0
- 把比较结果0赋值给shmid
后续传给shmat的shmid值恒为0,根本不是内核返回的合法共享内存标识符,系统校验参数不通过,直接返回Invalid argument错误。
- 指针类型声明逻辑完全错误
代码里data *dados[MAX]本质是包含MAX个data类型指针的数组,不是指向连续共享内存块的单指针。把shmat返回的映射地址赋值给数组第一个元素*dados后,后续用dados[i]->id的方式访问内存,本质是读取数组里每个未初始化的野指针指向的地址,哪怕shmat调用成功,也会触发段错误。 - shmat报错后未做退出处理
原代码shmat执行失败后没有return,会继续执行后续循环逻辑,访问无效内存区域,叠加报错现象。
修正后的可运行代码
#include <stdio.h> #include <sys/shm.h> #include <stdlib.h> #define MAX 10 // 需与写入端定义的MAX值保持一致 typedef struct { long id; } data; // 正确声明:指向连续共享内存块的单结构指针,而非指针数组 data *dados; int main() { key_t key = 5678; int shmid; // 加括号明确执行顺序:先完成shmid赋值,再判断返回值是否小于0 if ((shmid = shmget(key, MAX * sizeof(data), 0666)) < 0) { perror("shmget error"); return 1; } // 直接将shmat返回的映射地址赋值给单指针 if((dados = shmat(shmid, NULL, 0)) == (void*) -1) { perror("shmat"); return 1; // 报错后直接退出,避免无效内存访问 } for (int i = 0; i < MAX; i++) { printf("time:%ld\n", dados[i].id); printf("passed %d\n", i); } // 用完主动解除映射 shmdt(dados); return 0; }
额外校验排查项
- 执行
ipcs -m查看系统当前共享内存块列表,确认key=5678对应的块真实存在,块大小与两端约定的MAX * sizeof(data)一致 - 确认shmat的flag参数未传入非法值,默认传0即可
- 检查写入端是否在创建共享内存后、读取端接入前就执行了IPC_RMID删除操作,被提前删除的共享内存会导致接口校验失败
内容的提问来源于stack exchange,提问作者Filipe Coutinho
相关产品推荐
相关产品推荐

