linux,进程模型下,进程通过父进程产生子进程,子进程又产生子进程,子子孙孙又繁衍生息,每个子进程都有着各自的使命,相互配合,完成各项功能。当使命完成,子进程结束生命进程,被父进程回收。父进程调用wait()或者waitpid()系统函数取得子进程终止状态。如果一个子进程结束进程,他的父进程一直不读取他的退出信息,这些信息就会一直占用着资源,这些占用资源又不执行任务的进程,就会变成僵尸进程。
看看僵尸进程的定义:
进程的执行被终止,但是其父进程还没有使用wait()等系统调用来获知它的终止信息,此时进程成为僵尸进程
就好比一个人死亡,他的父亲总得知道他的死因,为他处理后事,但如果父亲无法为他及时处理后事,他就会一直在那占用着资源,携带者死亡的原因的信息,等待被人查看,着突然一个僵尸进程,他的父亲无法得之他的死因,也无法来替他收尸,他的遗体就一直在哪里占用着土地资源,成为一个僵尸。
在进程中同理,一个进程终止,总的有终止原因:是程序正常执行完终止,还是意外终止,总得有个说法,有信息反馈给父进程,在进程中,这些意外终止的信息存储在PCB中,
僵尸状态在结构体中的定义:EXIT_ZOMBIE
在这里补充进程的常见几种状态
状态 | 含义 |
---|---|
R | 正在执行状态 |
S | 浅度睡眠状态,可被唤醒, |
D | 深度睡眠,不可不唤醒,除非自己醒来 |
T | 暂停状态,不做任何事 |
Z | 退出状态。成为僵尸进程 |
X | 退出状态,进程即将被销毁 |
状态演示
写一个小程序,演示一下僵尸进程,用fork创建一个子进程。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid;
pid= fork(); //创建一个子进程
if(pid==0)
{
printf("i am a child process\n");
printf("pid:%d\tppid:%d\n",getpid(),getppid());
exit(0);
}
else if(pid>0)
{
printf("i am a father process\n");
printf("i will sleep five scores\n");
sleep(5); //父进程睡眠,等待子进程退出
system("ps -o pid,ppid,state,tty,command\n"); //输出进程信息
printf("father process is exited\n");
}
else
{
return -1;
}
return 0;
}
如果系统进程表一直被僵尸进程耗着,迟早会被耗尽,便无法创建新的进程,也无法执行其他任务,
僵尸进程的处理方法:
- 将子进程变成孤儿进程,从而他的父亲变成init进程,通过init进程处理僵尸进程
- 子进程退出时,向父亲发出“SIGGHILD”父亲处理”SIGGHILD”,在信号处理函数中调用wait()函数处理僵尸进程
孤儿进程
所谓孤儿进程,就是父进程先于子进程挂掉,子进程没有了父亲,变成了孤儿进程(orphan)
由于孤儿进程没有了父进程,当他终止时,没有父进程来回收他的资源,就会导致内存泄露,这是一个很严重的后果,为了解决这个问题,操作系统便派出1号进程领养该子进程。来处理子进程终止完后的资源回收。
下面来看下孤儿进程的演示效果
(还是以上面代码为例,这次我们让父进程先运行完)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int ret= fork();
if(ret==0)
{
printf("子进程PID:%d\n",getpid());
sleep(10);
exit(0);
}
else if(ret>0)
{
printf("父进程PPID:%d\n",getppid());
sleep(3);
}
else
return -1;
return 0;
}