首先需要知道三点:
1. 当某个信号的信号处理函数被调用时,该信号会被操作系统阻塞(默认sa_flags不设置SA_NODEFER标志) 。
2.当某个信号的信号处理函数被调用时,该信号阻塞时,该信号又多次发生,那么操作系统并不将它们排队,而是只保留第一次的,后续的被抛弃 。
还有一点我们必须清楚的是
3. wait系列函数与信号SIGCHLD是没有任何关系的,即wait系列函数并不是信号SIGCHLD驱动的 。
这个时候,肯定有人有疑问了,既然会丢弃信号,那怎么保证可以收回所有的僵尸进程呢?
关于这个问题,我们可以这样来理解,当子进程结束时,不管有没有产生SIGCHLD信号,或者子进程产生了SIGCHLD信号,而不管父进程有没有收到SIGCHLD信号,这都与子进程已经终止这个事实无关,就是说,子进程终止与信号其实没有任何关系,只是操作系统在子进程终止时会发送信号SIGCHLD给父进程,告之其子进程终止的消息,这样的话,父进程就可以做相应的操作了 。而wait系列函数的目的就是收回子进程终止时残留在进程列表中的信息,所以任何时候调用while ((pid = waitpid(-1, &exit_status, WNOHANG)) > 0)都可以收回所有的僵尸进程信息(可以参考下面的程序) 。但是这里为什么放在信号处理函数中处理了,这样做的原因是:子进程什么时候结束是个异步事件,而信号机制就是用来处理异步事件的,所以当子进程结束时,可以迅速的收回其残余信息,这样系统中就不会积累大量的僵尸进程了 。
也可以这样来理解:系统把所有的僵尸进程串在一起形成一个僵尸进程链表,而while ((pid = waitpid(-1, &exit_status, WNOHANG)) > 0)就是来清空这个链表的,直到waitpid()返回0,表明已经没有僵尸进程了,或者返回-1,表明出错(当错误码errno为ECHILD的时候同样表明已经不存在僵尸进程了) 。
了解了以上知识点,就能理解为什么while ((pid = waitpid(-1, &exit_status, WNOHANG)) > 0)能够回收所有的僵尸进程了 。
我们可以在上面的信号处理函数中加入相应的打印信息:
static int num1 = 0 static int num2 = 0; void avoid_zombies_handler(int signo) { pid_t pid; int exit_status; int saved_errno = errno; printf("num1 = %dn", ++num1); while ((pid = waitpid(-1, &exit_status, WNOHANG)) > 0) { printf("num2 = %dn", ++num2); } errno = saved_errno; }
【linux后台开发中避免僵尸进程的方法总结】打印的结果你会发现,当num1递增1的时候,即每调用一次信号处理函数,num2一般会递增很多,即while循环了很多次,所以尽管有的SIGCHLD信号被丢弃了,但是我们不用担心子进程的残余信息会收不回来 。退出while循环时,证明此时系统中已经没有僵尸进程了,所以退出信号处理函数后,阻塞的唯一SIGCHLD信号会再次触发该信号处理函数,这样我们就不用担心了 。我们不防做个最坏的打算,即之前的信号全部被丢弃了,只有最后一次的SIGCHLD信号被捕获,从而触发了信号处理函数,这样我们也不用担心,因为while循环会一次性收回全部的僵尸进程信息,只是这次循环的次数要多得多罢了,当然这只是假设,一般系统不会出现这样的情况(可以参考本文最后一个程序事例) 。
为了证明wait系统函数与信号SIGCHLD没有任何关系,我们可以做个简单的实验,代码如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> #include <sys/types.h> int main(int argc, char *argv[]) { int i; pid_t pid; for (i = 0; i < 5; i++) { if ((pid = fork()) == 0) /* child */ _exit(0); } sleep(10); while (waitpid(-1, NULL, WNOHANG) > 0) { /* do nothing */ } sleep(10); _exit(EXIT_SUCCESS); }
推荐阅读
- 10个很棒的JavaScript库,提升Web开发效率
- 科技之手点点,有机茶产业技术升级开发示范项目通过验收
- 用 testdisk 恢复 Linux 上已删除的文件
- 记一次使用next.js开发官网经历
- 使用 GNU bc 在 Linux Shell 中进行数学运算
- springcloud微服务架构开发实战:分布式消息总线
- 开发古茶山旅游,通关古茶山
- 谈谈如何学习Linux
- 如何获取Linux或者macOS系统版本相关信息
- 小程序云开发支持公众号网页开发了