1.什么是僵尸进程
一个进程使用fork()创建子进程,如果子进程退出,而父进程并没有调用wait()或waitpid()获取子进程的状态信息,那么子进程的某些信息如进程描述符仍然保存在系统中,这种进程称之为僵尸进程
2.实现僵尸进程
在main函数中,创建子进程,然后让父进程睡眠10s,让子进程先终止(注意和孤儿进程例子的区别),这里子进程结束后父进程没有调用wait()/waitpid()函数获取其状态,用ps查看进程状态可以看出子进程为僵尸状态
3.代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
int main()
{
pid_t pid;
pid = fork();
if (pid < 0)
{
perror("fork failed");
exit(1);
}
if (pid == 0)
{
printf("I am child process.\n");
printf("I am eexited.\n");
exit(0);//先让子进程结束
}
printf("I am father process.\n");
sleep(10);//父进程睡眠10s等待子进程退出,又没调用wait()或waitpid()获取其状态
子进程成为僵尸进程
printf("father process is exited.\n");
return 0;
}
4.运行结果:
这两个运行结果是程序的运行结果,它们之间会有10s的等待,我们可以用此时间来查看子进程到底有没有成为僵尸进程。
查看僵尸进程的方法:在等待的10s中在另一个终端敲命令ps aux | grep a.out(ps aux | grep name是查看进程的状态的),就可以查看进程的状态
5.僵尸进程的危害
僵尸进程会在系统中保留其某些信息如进程描述符、进程id等等。以进程id为例,系统中可用的进程id是有限的,如果由于系统中大量的僵尸进程占用进程id,就会导致因为没有可用的进程id系统不能产生新的进程,这种问题可就大了,这就是僵尸进程来的危害,因此大部分情况下,我们都应当避免僵尸进程的产生。
ps:总而言之,僵尸进程带给我们最大的危害就是内存泄露
6.怎么避免僵尸进程的发生
(1)在父进程中显式的读取子进程的状态
示例1
#include<stdio.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
void handler(int sig)
{
pid_t ret=0;
while((ret = waitpid(-1,NULL,WNOHANG))>0)//非阻塞式的等待子进程执行结束
{
printf("wait child success!ret is %d\n",ret);
}
printf("child quit! pid is %d\n",getpid());
}
int main()
{
signal(SIGCHLD,handler);
pid_t pid=fork();
if(pid==0)
{
printf("I am child process. pid is %d\n",getpid());
sleep(3);
exit(0);
}
while(1)
{
printf("father process can something\n");
sleep(1);
}
return 0;
}
运行结果:
(2)用signal函数将SIGCHLD信号的处理动作置为SIG_IGN,这样fork出来的进程在终止时会自动清理掉,不会产生僵尸进程,也不会通知父进程
示例2
#include<stdio.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
int main()
{
signal(SIGCHLD,SIG_IGN);
pid_t pid=fork();
if(pid==0)
{
printf("I am child process. pid is %d\n",getpid());
sleep(3);
exit(0);
}
while(1)
{
printf("father process can something\n");
sleep(1);
}
return 0;
}
运行结果:
解析:子进程执行结束,没有通知父进程,此时的进程并没有变为僵尸进程
7.怎么消灭僵尸进程
消灭父进程,那僵尸进程(子进程)就会变为孤儿进程,孤儿进程就会被init进程收养
8.进程的相关知识
任何一个子进程(init进程除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程的数据结构,等待父进程处理,这是每个子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是"Z".如果父进程能及时处理,可能用ps命令就来不及看到子进程的僵尸状态,如果父进程在子进程结束之前退出,则子进程将由init接管,init将会以父进程的身份对僵尸状态的子进程进行处理