文章目录
一、僵死进程
(一)什么是僵死进程
1. 僵死进程的产生
一个进程执行结束,但是进程的PCB还没有被系统释放,因为进程结束后,在PCB中还要保存进程退出码,以备其父进程获取其退出码,那么就是父进程未结束,子进程结束,所以此时父进程没有获取子进程的退出码。即一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,释放所占资源,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
我们可以用一个代码来看一下僵死进程长什么样子。我们fork一个子进程,让它睡眠10s结束,父进程睡眠20s,这样子进程会比父进程先结束,子进程就会变成一个僵死进程。那么代码如下:
# include<stdio.h>
# include<stdlib.h>
# include<unistd.h>
# include<assert.h>
int main()
{
pid_t pid=fork();
assert(pid!=-1);
if(pid==0)//子进程返回PID=0
{
printf("i am child\n");
sleep(10);
printf("child over\n");
}
else//父进程返回子进程的PID
{
printf("i am father\n");
sleep(20);
printf("father over");
}
exit(0);
}
2. 查看僵死进程
我们将上面的代码放到后台运行,在前台查看进程运行状态,可以用ps查看进程状态,也可以用top查看整体状态。
- ps查看
僵死进程的表示为defunct。 - top查看僵死进程
我们需要清楚一个概念:任何一个子进程(除init进程,它是所有进程的父进程)在exit()后,并非马上就消失掉,会留下一个僵尸进程(Zombie)的数据结构,等待父进程处理,这是每个进程都要经历的阶段,如果父进程处理及时,那么我们看不到,但不代表它没有经历这个阶段。
3. 僵死进程的危害&&区分孤儿进程和僵死进程
(1)僵死进程的危害:
先结束的子进程不会自己释放占用的PCB资源,如果父进程不调用wait/waitpid,那么这段信息不会释放,PID就会被占用,但是一个系统能使用的进程号是有限的,如果产生大量的僵死进程,那么系统就不能产生新的进程了,系统就会崩溃,资源被一直浪费,这就是危害。
(2)孤儿进程:
一个父进程退出,但是它的子进程还在运行,那么这些进程称为孤儿进程,内核会将孤儿进程的父进程设为init进程,孤儿进程被init进程(进程号为1)处理,init会循环的wait()这些孤儿进程,所以孤儿进程没有危害。
(二)处理僵死进程
当一个进程正常或异常终止时,内核就像其父进程发送SIGCHLD信号,子进程终止是一个异步事件,即可以在父进程运行的任何时候发生,所以这种信号是内核向父进程发的异步通知,父进程可以选择忽略该信号,或者提供一个该信号发生时被调用执行的函数,我们后面会对信号量处理僵死进程
进行讲解。主要处理僵死进程的执行函数是wait和waitpid函数。头文件为:# include<sys/wait.h>
1.wait函数
1.函数原型:
pid_t wait(int *statloc)
2.参数:
statloc是一个整型指针,如果传入指针进去,那么进程的终止状态就会存放在它所指向的内存空间内;如果传入空指针,表示不关心终止状态。
成功返回子进程的PID,如果没有子进程调用会失败返回-1,同时errno被置为ECHILD。
3.作用:
父进程一旦调用了wait,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里&#x