前言
在上篇中,我们已经发现了原来的并发回射程序隐藏的问题 - 僵尸子进程,而在这下篇中,我们将通过Linux的信号机制,将这些不该存于世间的“ 僵尸 ”通通清理掉。
清理思路
在Linux中,每当子进程被撤销,它都会发送一个SIGCHLD信号给内核。我们只需要设置相应机制捕获这个信号然后命令系统彻底清理掉原来的子进程即可。
第一步:定义注册函数
函数作用:关联信号和信号处理函数
函数定义:
1 #include "unp.h" 2 3 Sigfunc * 4 signal(int signo, Sigfunc *func) 5 { 6 struct sigaction act, oact; 7 8 // 记录信号处理函数 9 act.sa_handler = func; 10 // 设置额外阻塞信号 11 sigemptyset(&act.sa_mask); 12 // 设置信号处理的其他相关操作 13 act.sa_flags = 0; 14 15 if (signo == SIGALRM) { 16 #ifdef SA_INTERRUPT 17 // 设置被信号所中断的系统调用不自动重启 18 act.sa_flags |= SA_INTERRUPT; 19 #endif 20 } else { 21 #ifdef SA_RESTART 22 // 设置被信号所中断的系统调用自动重启 23 act.sa_flags |= SA_RESTART; 24 #endif 25 } 26 // 正是注册信号及信号的处理函数 27 if (sigaction(signo, &act, &oact) < 0) 28 return(SIG_ERR); 29 // 返回信号处理函数指针 30 return(oact.sa_handler); 31 }
第二步:定义信号处理函数
函数作用:接收到指定信号后撤销僵尸子进程
函数定义:
1 #include "unp.h" 2 3 void 4 sig_chld(int signo) 5 { 6 pid_t pid; 7 int stat; 8 9 // waitpid 函数中,-1表示即将终止的子进程,stat返回子进程的终止状态,WNOHANG参数告知内核在没有已终止内核时不要阻塞。 10 while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0) 11 printf("child %d terminated ", pid); 12 return; 13 }
注:waitpid包含于头文件 sys/wait.h
第三步:修改服务器代码并启用清理机制
我们需要添加以下几个部分代码:1. 调用注册函数 2. 为了便于移植,对一些慢中断函数手工自动重启。
运行测试:
1. 在一个终端以超级用户权限启动服务器
2. 在另几个终端打开客户端并输入IP地址参数127.0.0.1
3. 执行回射测试,每当有客户结束终端时,显示情况如下:
表示这些子进程都被真的“ 杀死 ”了。再查看当前进程状态验证之:
哦也,果然没有僵尸进程了。