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函数只能回收一个进程。