子进程的回收

为何要回收子进程

一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。如果父进程不去回收子进程,那么它就会变成僵尸进程,占用系统资源。
这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。
我们知道一个进程的退出状态可以在Shell中用特殊变量查看,因为Shell是它的父进程,当它终止时Shell调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。

函数原语

下面介绍一下两个常用的回收函数
(1)wait

#include 
#include 

pid_t wait(int *status);
  • status为传出参数,保存终止信息
  • 成功返回子进程号,失败返回-1,设置errno
    wait的几个作用
    1.阻塞等待子进程退出
    2.回收子进程的残留资源
    3.获取子进程的结束状态
    4.一次只能回收一个

(2)waitpid

pid_t waitpid(pid_t pid, int *status, int options);
  • pid为要回收的僵尸子进程的进程号
    小于 -1 回收指定进程组内的任意子进程
    -1 回收和当前调用waitpid一个组的任意子进程
    0 回收和当前调用waitpid一个组的所有子进程
    大于 0 回收指定ID的子进程
  • options为属性设置参数
    WNOHANG非阻塞,立刻返回
    0 阻塞,与wait相同
  • 返回值
    如果options设置为WNOHANG
    有僵尸子进程返回子进程号
    无僵尸子进程返回0
    如果options设置为0
    成功返回子进程号
    失败返回-1

退出状态分析

常见的两种退出状态

  1. 正常退出
    WIFEXITED(status)—判断是否正常退出
    WEXITSTATUS(status)—返回正常退出的退出值

  2. 信号退出
    WIFSIGNALED(status)–判断是否被信号杀死
    WTERMSIG(status)–返回被杀死的信号编号

代码演示

#include 
#include 
#include 
#include 
#include 

int main()
{
		pid_t pid=fork();
		if(pid>0)
		{
				int status;
				pid_t wpid=wait(&status);
				if(wpid<0)
				{
						perror("wait err");
						exit(1);
				}
				else
				{
						if(WIFEXITED(status))
						{
								printf("child exit with%d\n",WEXITSTATUS(status));
						}
						if(WIFSIGNALED(status)
						{
								printf("child exit with signal %d\n",WTERMSIG(status));
						}
				}
				while(1)
				{
						sleep(1);
				}
		}
		else if(pid==0)
		{
				sleep(5);
				exit(5);
		}
		else
		{
				perror("fork err");
				exit(1);
		}
		return 0;
}

等待5s后正常退出,返回child exit with5
使用kill信号杀死进程,返回child exit with signal 9
子进程退出之后会向父进程发出SIGCHILD信号,一般可以通过捕捉这个信号进行进程回收

#include 
#include 
#include 
#include 
void wait_catch(int num)//信号捕捉处理函数,回收子进程
{
		pid_t wpid;
		while((wpid=waitpid(-1,NULL,WNOHANG))>0)
		{
				printf("wait child%d,signal%d\n",wpid,num);
		}
}
int main()
{
		sigset_t my_set,old_set;
		int i=0;
		sigemptyset(&my_set);
		sigaddset(&my_set,SIGCHLD);
		sigprocmask(SIG_BLOCK,&my_set,&old_set);
		for(i=0;i<10;++i)
		{
			pid_t pid=fork();
			if(pid==0)
			{
					break;
			}
		}
		if(i==10)
		{
				sleep(4);
				struct sigaction act;
				act.sa_handler=wait_catch;
				act.sa_flags=0;
				sigemptyset(&act.sa_mask);
				sigaction(SIGCHLD,&act,NULL);//注册信号捕捉函数
				sigprocmask(SIG_SETMASK,&old_set,NULL);
				while(1)
						sleep(1);
		}
		else if(i<10)
		{
				printf("im child %d\n",getpid());

		}
		return 0;
}

你可能感兴趣的:(Linux系统编程)