文章目录
僵尸进程概念
在 unix/linux 中,正常情况下,子进程是通过父进程创建的。子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底什么时候结束。当一个进程完成它的工作终止之后,它的父进程需要调用 wait()
或者 waitpid()系统调用取得子进程的终止状态。
僵尸进程:一个进程使用 fork 创建子进程,如果子进程退出,而父进程并没有调用 wait 或 waitpid 获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死(zombie)进程。
僵尸进程问题及危害
unix提供了一种机制可以保证只要父进程想知道子进程结束时的状态信息,就可以得到。
这种机制就是: 在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。
但是仍然为其保留一定的信息(包括进程号 the process ID,退出状态 the termination status of the process,运行时间 the amount of CPU time taken by the process 等)。直到父进程通过 wait / waitpid 来取时才释放。
但这样就导致了问题,如果进程不调用 wait / waitpid 的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。
实现一个僵尸进程
详细内容请移驾另一篇博客:
https://yangyongli.blog.csdn.net/article/details/118738169
僵尸进程处理
我们知道僵尸进程是不能通过 kill 命令来终止的,想要处理系统中的僵尸进程,只能找到产生僵尸进程的父进程,使用 kill 命令终止父进程即可。此时僵尸进程成为孤儿进程,会被 init 进程接管,调用 wait 函数释放资源。
僵尸进程的预防
(1)通过信号机制
子进程退出时向父进程发送 SIGCHILD 信号,父进程处理 SIGCHILD 信号。
在信号处理函数中调用 wait 进行处理僵尸进程。测试程序如下所示
通过信号量及时发现了僵尸进程并处理了。
/*************************************************************************
> File Name: main.c
> Author: 杨永利
> Mail: 1795018360@qq.com
> Created Time: 2021年07月14日 星期三 21时54分29秒
************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
static void sig_child(int signo);
int main()
{
pid_t pid;
//创建捕捉子进程退出信号
signal(SIGCHLD, sig_child);
pid = fork();
if (pid < 0)
{
perror("fork error:");
exit(1);
}
else if (pid == 0)
{
printf("I am child process,pid id %d.I am exiting.\n",getpid());
exit(0);
}
printf("I am father process.I will sleep two seconds\n");
//等待子进程先退出
sleep(2);
//输出进程信息
system("ps -o pid,ppid,state,tty,command");
printf("father process is exiting.\n");
return 0;
}
static void sig_child(int signo)
{
pid_t pid;
int stat;
//处理僵尸进程
while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
printf("child %d terminated.\n", pid);
}
(2)Fork 两次
《Unix 环境高级编程》8.6 节说的非常详细。原理是将子进程成为孤儿进程,从而其的父进程变为 init 进程,通过 init 进程可以处理僵尸进程。测试程序如下所示:
/*************************************************************************
> File Name: main.c
> Author: 杨永利
> Mail: 1795018360@qq.com
> Created Time: 2021年07月14日 星期三 22时08分39秒
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main()
{
pid_t pid;
//创建第一个子进程
pid = fork();
if (pid < 0)
{
perror("fork error:");
exit(1);
}
//第一个子进程
else if (pid == 0)
{
//子进程再创建子进程
printf("I am the first child process.pid:%d\tppid:%d\n", getpid(), getppid());
pid = fork();
if (pid < 0)
{
perror("fork error:");
exit(1);
}
//第一个子进程退出
else if (pid > 0)
{
printf("first procee is exited.\n");
exit(0);
}
//第二个子进程
//睡眠 3s 保证第一个子进程退出,这样第二个子进程的父亲就是 init 进程里
sleep(3);
printf("I am the second child process.pid: %d\tppid:%d\n", getpid(), getppid());
exit(0);
}
//父进程处理第一个子进程退出
if (waitpid(pid, NULL, 0) != pid)
{
perror("waitepid error:");
exit(1);
}
exit(0);
return 0;
}