1、进程资源为什么要回收?
当一个进程退出之后,进程能够回收自己的用户区的资源,但是不能回收内核空间的PCB资源,必须由它的父进程调用wait或者waitpid函数完成对子进程的回收,避免造成系统资源的浪费。
2、孤儿进程和僵尸进程
2.1 孤儿进程
- 若子进程的父进程已经死掉,而子进程还存活着,这个进程就成了孤儿进程。
- 为了保证每个进程都有一个父进程,孤儿进程会被init进程(0号进程)领养,init进程成为了孤儿进程的养父进程,当孤儿进程退出之后,由init进程完成对孤儿进程的回收。
2.2 僵尸进程概念
若子进程死了,父进程还活着, 但是父进程没有调用wait或waitpid函数完成对子进程的回收,则该子进程就成了僵尸进程。
2.3 如何解决僵尸进程
- 由于僵尸进程是一个已经死亡的进程,所以不能使用kill命令将其杀死。
- 通过杀死其父进程的方法可以消除僵尸进程。 杀死其父进程后,这个僵尸进程会被init进程领养,由init进程完成对僵尸进程的回收。
3、进程回收函数
3.1 wait函数
3.1.1 函数原型
pid_t wait(int *status);
3.1.2 函数作用
- 阻塞并等待子进程退出 (说明这个函数是阻塞的).
- 回收子进程残留资源.
- 获取子进程结束状态(结束原因)。
3.1.3 返回值
- 成功:清理掉的子进程ID;
- 失败:-1(没有子进程)。
3.1.4 函数参数
status参数:子进程的退出状态 ——传出参数
- WIFEXITED(status):为非0 → 进程正常结束
- WEXITSTATUS(status):获取进程退出状态 (正常退出状态是0)
- WIFSIGNALED(status):为非0 → 进程异常终止
- WTERMSIG(status):取得进程终止的信号编号。
上述参数的宏可以通过man命令查看,不必硬记。
3.1.5 wait函数代码实例
//进程回收函数——wait
//父进程调用wait函数完成对子进程的回收
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
//创建子进程
//pid_t fork(void)
pid_t pid = fork();
if(pid<0)
{
perror("fork error");
return -1;
}
else if(pid>0)//父进程
{
printf("father: pid == [%d]\n",getpid());
int status;
pid_t wpid = wait(&status);
printf("wpid==[%d]\n",wpid);
if (WIFEXITED(status)) //正常退出
{
printf("child normal exit,status==[%d]\n",WEXITSTATUS(status));
}
else if(WIFSIGNALED(status)) //子进程被信号杀死
{
printf("child kiled by signal,signo==[%d]\n",WTERMSIG(status));
}
}
else if(pid==0) //子进程
{
printf("child: pid==[%d],fpid==[%d]\n",getpid(),getppid());
sleep(5);
//return 9;
}
return 0;
}
3.2 waitpid函数
3.2.1 函数原型
pid_t waitpid(pid_t pid, int *status, in options);
3.2.2 函数作用
同wait函数。
3.2.3 返回值
- >0:返回回收掉的子进程ID。
- -1:无子进程。
- 0:参数3位WNOHANG,子进程正在运行。
3.2.4 函数参数
pid:
pid = -1 等待任一子进程。与wait等效。
pid > 0 等待其进程ID与pid相等的子进程。
pid = 0 等待进程组ID与目前进程相同的任何子进程,也就是说任何和调用 waitpid()函数的进程在同一个进程组的进程。
pid < -1 等待其组ID等于pid的绝对值的任一子进程。(适用于子进程在其他组的情况)
- status:子进程的退出状态,用法同wait函数。
- options:设置为WNOHANG,函数非阻塞,设置为0,函数阻塞。
3.2.5 waitpid代码实例
//进程回收函数——waitpid
//父进程调用waitpid函数完成对子进程的回收
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
//创建子进程
//pid_t fork(void)
pid_t pid = fork();
if(pid<0)
{
perror("fork error");
return -1;
}
else if(pid>0)//父进程
{
printf("father: pid == [%d]\n",getpid());
int status;
//pid_t waitpid(pid_t pid, int *status, in options);
while(1)
{
pid_t wpid = waitpid(-1,&status,WNOHANG); //-1表示等待任一子进程,WNOHANG表示不阻塞
printf("wpid==[%d]\n",wpid);
if(wpid>0) //子进程被回收
{
if (WIFEXITED(status)) //正常退出
{
printf("child normal exit,status==[%d]\n",WEXITSTATUS(status));
}
else if(WIFSIGNALED(status)) //子进程被信号杀死
{
printf("child kiled by signal,signo==[%d]\n",WTERMSIG(status));
}
}
else if(wpid==0) //子进程还活着
{
printf("child is living,wpid==[%d]\n",wpid);
}
else if(wpid==-1) //没有子进程了
{
printf("no child is living,wpid==[%d]\n",wpid);
break;
}
}
sleep(1);
}
else if(pid==0) //子进程
{
printf("child: pid==[%d],fpid==[%d]\n",getpid(),getppid());
sleep(2);
return 9;
}
return 0;
}
注意:
调用一次waitpid和wait函数只能回收一个进程。