C语言开发自定义Shell时子进程结束变为僵尸进程问题咨询
问题根因
- 僵尸进程产生原因:Linux系统中,子进程退出后父进程必须调用
wait()/waitpid()读取其退出状态,否则子进程会残留为僵尸进程,直到父进程退出才会被init进程回收。你当前的代码仅在fork完成后立刻调用了一次带WNOHANG参数的waitpid(),此时sleep进程还未执行完成,waitpid()直接返回0,之后你再也没有对该子进程做状态回收,所以子进程退出后就变成了僵尸进程。 - 没有打印完成提示的原因:你把打印逻辑放在了第一次调用
waitpid()之后,此时子进程还没退出,代码直接执行了打印逻辑,自然不会等到10秒后子进程退出才触发打印。
解决方案
通过捕捉SIGCHLD信号实现后台进程的自动回收和完成提示,SIGCHLD是子进程退出时系统主动发送给父进程的信号,我们可以在该信号的处理函数中完成子进程状态回收和提示打印。
具体实现步骤
- 引入必要头文件
#include <signal.h> #include <sys/wait.h>
- 实现
SIGCHLD信号处理函数
void sigchld_handler(int sig) { int status; pid_t exited_pid; // 循环回收所有已退出的后台进程,避免同一时间多个子进程退出漏回收 while ((exited_pid = waitpid(-1, &status, WNOHANG)) > 0) { if (WIFEXITED(status)) { printf("\n后台进程 %d 已执行完成,退出码:%d\n", exited_pid, WEXITSTATUS(status)); // 重新打印shell提示符,避免提示输出后终端没有输入引导 printf(": "); fflush(stdout); } } }
- 在shell主逻辑初始化阶段注册信号处理函数
int main() { struct sigaction sa; sa.sa_handler = sigchld_handler; sigemptyset(&sa.sa_mask); // SA_RESTART标志让被信号中断的系统调用(比如读取用户输入的read)自动重启,避免报错 sa.sa_flags = SA_RESTART; sigaction(SIGCHLD, &sa, NULL); // 你原有shell的主循环逻辑 while(1) { // 读取输入、解析命令、fork执行的原有逻辑 // ... else { // 父进程分支,删除原有的waitpid和完成提示打印逻辑,只保留后台pid打印即可 printf("background pid is %d\n", childPid); } } return 0; }
效果说明
修改完成后,后台sleep进程运行结束时,系统会触发SIGCHLD信号,自动执行处理函数回收子进程,不会产生僵尸进程,同时会打印进程完成提示。
内容的提问来源于stack exchange,提问作者Jorge Jones
相关产品推荐
相关产品推荐

