exec后子进程编辑父进程共享内存失败的原因排查咨询
结合你的代码和描述,我整理了几个最可能导致问题的原因,按优先级排序:
1. 错误的shmat返回值判断(最可能触发问题)
你的子进程代码里用if(!shared_mem) return;来检测共享内存挂载是否成功,这是一个典型的错误。shmat()调用失败时返回的是(void *)-1,而不是NULL——当把这个值转换成int*类型时,它并不是空指针,所以你的判断条件根本不会触发,会直接执行后续的写入操作。但此时shared_mem指向的是无效地址,自然会表现出“无法编辑共享内存”的现象。
正确的检测逻辑应该是这样:
int *shared_mem = (int*)shmat(shm_id, 0, NULL); // 必须用(void*)-1来判断shmat失败 if (shared_mem == (void*)-1) { perror("shmat failed"); // 打印具体错误原因,方便排查 return; }
2. 共享内存实际权限被umask修改
你在父进程中给shmget传了0777权限,但实际创建的共享内存权限会被父进程的umask值掩码处理。比如如果父进程的umask是0022,那么实际权限会变成0755(其他用户仅有读权限)。如果exec后的子进程运行身份(用户/组)和父进程不一致,就会没有写入权限,导致即使shmat成功,写入操作也会失败。
你可以用ipcs -m命令查看系统中共享内存的实际权限,确认是否符合预期。
3. 父进程提前销毁了共享内存
如果父进程在fork后没有等待子进程完成,直接退出,并且在退出前调用了shmctl(shm_id, IPC_RMID, NULL)标记共享内存删除——那么当父进程(最后一个挂载该共享内存的进程)退出后,系统会立即销毁共享内存。此时子进程拿到的shm_id虽然正确,但对应的共享内存已经不存在了,shmat会失败(但你的错误判断没检测到)。
检查父进程是否有提前退出的逻辑,或者误加了删除共享内存的代码。
4. exec后进程有效身份变化导致权限不足
如果execve执行的程序带有setuid或setgid位,子进程的有效用户/组会切换为程序文件的所有者。比如共享内存是普通用户创建的,但exec的程序是root所有,此时子进程有效UID变成root(有权限操作);但反过来,如果共享内存是root创建的,exec后子进程有效UID是普通用户,就会没有写入权限。这种情况可以通过perror打印的错误码快速确认。
内容的提问来源于stack exchange,提问作者gblaster

