execve调用未启动子进程、消息队列无写入数据问题排查
问题根因
你的代码存在3个核心致命问题,导致子进程根本没执行到user.c的逻辑就提前崩溃:
- 子进程逻辑里存在野指针崩溃:你定义了
char* test;但没有给它分配任何合法内存,直接调用sprintf(test, "%d", *sh_value);会向随机内存地址写入数据,直接触发段错误(SIGSEGV),子进程在调用execve之前就被内核杀死,user程序根本没有被加载运行——这就是为什么你在user.c里加while(1)死循环也没用,代码根本没跑到那一步。 - 所有系统调用都没有错误处理:
execve、msgget、msgrcv、fork这些调用失败时你没有打印任何错误日志,完全感知不到子进程崩溃、程序加载失败这类问题。 - 父进程收消息逻辑存在竞态:你用
IPC_NOWAIT非阻塞读消息队列,同时用WNOHANG非阻塞等待子进程,循环没有任何让出CPU的操作,CPU会被全速占用,大概率子进程还没来得及发消息,父进程就已经因为收完3个退出的子进程直接退出。 - 额外小问题:
execve第三个参数传NULL在部分环境下会导致子进程启动异常,且共享内存的*sh_value递增操作没有加锁,多进程同时执行会出现计数竞态。
修复步骤
- 修复野指针问题:给字符串缓冲区分配合法内存,不要使用未初始化的指针存储
sprintf的结果 - 给
execve增加错误判断:execve只有执行失败才会返回,一旦返回立刻打印错误并退出子进程,避免子进程跑入父进程的代码逻辑 - 调整父进程收消息逻辑,避免忙等漏收消息
- 确保编译时同时生成
master和user两个可执行文件,放在同一目录下运行
修复后的核心代码
master.c 子进程分支修复
case 0: // 替换原来的野指针,在栈上分配足够存整数转字符串的内存 char test[16]; *sh_value = *sh_value +1; sprintf(test,"%d",*sh_value); args[1] = test; args[2] = NULL; execve(CHILD_NAME,args,environ); // 走到这里说明execve执行失败,打印错误直接退出子进程 perror("execve user process failed"); _exit(1); break;
父进程收消息逻辑优化(可选,避免忙等)
int i = 0; int recv_count = 0; while(i < SO_USERS_NUM){ num_bytes = sizeof(mt_msg) - sizeof(mt_msg.mtype); num_bytes = msgrcv(q_id, &mt_msg, num_bytes, 3, IPC_NOWAIT); if (num_bytes >= 0) { printf("received message"); if (num_bytes >= sizeof(mt_msg.numero)) printf(" numero: %d", mt_msg.numero); printf("\n"); recv_count++; } pid_t pid_child = waitpid(-1,NULL,WNOHANG); if (pid_child > 0) { printf("\nProcesso figlio terminato: %d",pid_child); ++i; } // 让出CPU,避免忙等占满资源,给子进程执行时间 if (recv_count < SO_USERS_NUM) { usleep(1000); } }
验证方法
编译命令:
gcc master.c -o master gcc user.c -o user ./master
正常运行后会看到3条消息接收打印,以及3条子进程退出的提示。
内容的提问来源于stack exchange,提问作者Matteo Pagliarello
相关产品推荐
相关产品推荐

