僵尸进程就是父进程创建了一个子进程,但是子进程退出以后,父进程在忙着干自己的事,没有回收子进程,导致子进程一直占用内存资源,这种情况我们称之为僵尸进程。
第一种方式,使用waitpid函数让父进程停下来等待子进程退出,并回收子进程,此时就能解决僵尸进程。在学习了进程信号以后,就有了第二种方式,子进程在退出的时候,会给父进程发送一个SIGCHLD信号,这个时候我们可以使用处理信号的知识来解决这个问题。
目录
2、方式二:设置信号处理方式为SIG_IGN(仅适用于Linux)
一、waitpid函数
下面是waitpid函数的声明以及需要用到的头文件,我们要重点介绍的是,这个函数的参数
1、 第一个参数:准备回收的子进程的pid
如果填入的是子进程的pid,那么就会等待回收指定进程的pid
waitpid(19989, ... , ...); //等待进程pid为19989的子进程
如果填入的是 -1,那么就会等待回收所有进程
2、第二个参数:输出型参数,获取子进程的退出状态
int status;
waitpid(-1 , &status , ... );
status不能简单的当作整型来看,要从二进制的角度来看,子进程正常退出时,子进程返回的是退出码;异常退出时,子进程返回的是一个中止信号。status以二进制的形式保存了这两样东西,保存在低16位,即0~15位,其中 0~6 保存中止信号,7 是core dump标志位,8~15是保存退出码。
想要详细了解的可以看看下面博客中的 “关于status”
3、 第三个参数:进程等待的方式
进程的等待方式分为阻塞等待和非阻塞等待,阻塞等待就是执行waitpid以后,父进程啥也不干,干等着,等回收完子进程再继续向下运行;非阻塞等待就是执行waitpid以后,每隔一段时间,检测一下子进程是否退出,如果退出了,就回收,没退出,就继续干自己的事。
想要详细了解的可以看看下面博客中的 “进程阻塞等待的方式”、“进程非阻塞等待的方式”(内含测试代码)
二、SIGCHLD信号(17号信号)
1、方式一:在信号处理函数中,使用wait回收进程
waitpid阻塞方式等待的弊端就是,父进程阻塞了,就不能干自己的事了;waitpid非阻塞等待的弊端是,在处理自己的事的同时需要时不时检测一下子进程是否退出,程序实现比较复杂。
实际上子进程在退出的时候,会给父进程发送SIGCHLD信号,默认动作是忽略(handler表填的是SIG_DFL),父进程可以自定义SIGCHLD的信号处理函数,这样的话,就可以专注于自己的工作,不必关心子进程的退出了
下面是测试代码
预期效果是,3s以后子进程退出,父进程一直在干自己的事,既没有阻塞,实现起来也不是特别复杂,最重要的是不会出现僵尸进程,下面我们会从代码结果的角度、进程变化的角度观察
刚开始进程是这样
随后会变成下面这样,自始至终没有出现过Z状态
2、方式二:设置信号处理方式为SIG_IGN(仅适用于Linux)
除了上述的处理方式外,还有一种更为简单的方式,一开始17号信号选择的是默认处理方式SIG_DFL,我们可以把17号的处理方式显式的设置为SIG_IGN,表明父进程不关心子进程的退出信息,只要回收了就就行
测试结果如下,依然没有出现 僵尸进程