接上一篇:linux_exec函数族-execl函数-execlp函数-execle函数-execv函数-execvp函数-execve函数
今天来向大家分享几个有趣的知识点,一个是孤儿进程,一个是僵尸进程,hhh,是不是很有趣,另外再来看看怎么去回收子进程,开始上菜:
目录
- 1.孤儿进程
- 2.僵尸进程
- 3.wait函数
- 例子-回收正常进程:
- 例子-回收异常进程:
 
- 4.waitpid函数
- 例子-阻塞回收一个进程:
- 例子-阻塞等待回收所有子进程:
- 例子-非阻塞等待回收子进程:
 
1.孤儿进程
  孤儿进程: 父进程先于子进程结束,则子进程成为孤儿进程,此时子进程的父进程就成为init进程,该子进程就相当于被init进程领养。
   init进程是linux内核启动的第一个进程。
2.僵尸进程
  僵尸进程: 进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸(Zombie)进程。
 红框中:
   S:在后台运行
   R:正在运行
   Z:僵尸进程,已死亡状态
   defunct:该进程死亡了
 
3.wait函数
  一个进程在结束时,是需要父进程进行回收的,此时可以调用wait函数来进行回收。
 当进程终止时,操作系统的隐式回收机制会:
   1.关闭所有文件描述符;
   2. 释放用户空间分配的内存。内核的PCB仍存在。其中保存该进程的退出状态。(正常终止→退出值;异常终止→终止信号)
 函数作用:
   ① 阻塞等待子进程退出
   ② 回收子进程残留资源
   ③ 获取子进程结束状态(退出原因)。
 头文件:
   #include <sys/types.h>
   #include <sys/wait.h>
 函数原型:
   pid_t wait(int *status);
 函数参数:
   status:用来保存进程退出的状态
 可使用wait函数传出参数status来保存进程的退出状态。借助宏函数来进一步判断进程终止的具体原因。宏函数可分为如下三组:
1.  WIFEXITED(status) 为非0	→ 进程正常结束
	WEXITSTATUS(status) 如上宏为真,使用此宏 → 获取进程退出状态 (exit的参数)
2. 	WIFSIGNALED(status) 为非0 → 进程异常终止
	WTERMSIG(status) 如上宏为真,使用此宏 → 取得使进程终止的那个信号的编号。
3. 	WIFSTOPPED(status) 为非0 → 进程处于暂停状态
	WSTOPSIG(status) 如上宏为真,使用此宏 → 取得使进程暂停的那个信号的编号。
	WIFCONTINUED(status) 为真 → 进程暂停后已经继续运行
一次wait调用,只回收一个子进程,阻塞状态回收子进程,也就是调用该函数,会停在这里等待回收子进程。
返回值:
   成功返回被终止子进程的进程ID;
   错误返回-1。
例子-回收正常进程:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
int main(void)
{
	pid_t pid, wpid;
	pid = fork();
	if(pid == -1)
	{
		perror("fork error");
		exit(1);
	} 
	else if(pid == 0)
	{		//子进程
		printf("子进程ID = %d\n", getpid());
		sleep(5);				
	} 
	else 
	{
		wpid = wait(NULL);	//阻塞等待回收子进程	
		if(wpid == -1)
		{
			perror("wait error");
		}
		printf("父进程回收的子进程ID = %d\n", wpid);
	}
	return 0;
}
例子-回收异常进程:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
int main(void)
{
	pid_t pid, wpid;
	int status;
	pid = fork();
	if(pid == -1)
	{
		perror("fork error");
		exit(1);
	} 
	else if(pid == 0)
	{		//son
		printf("我是子进程, pid = %d\n", getpid());
#if 0
		//错误退出测试
		execl("./xxx", "abnor", NULL);
		perror("execl error");
		exit(1);
#endif
		sleep(1);				
		exit(10);
	} 
	else 
	{
		//wpid = wait(NULL);	//传出参数
		wpid = wait(&status);	//传出参数
		if(WIFEXITED(status))
		{	//正常退出
			printf("我是父进程,子进程 %d 正常退出!\n", wpid);
			printf("返回值: %d\n", WEXITSTATUS(status));
		} 
		else if (WIFSIGNALED(status)) 
		{	
			//异常退出
			printf("子进程 %d 异常退出!返回值为:%d\n", wpid, WTERMSIG(status));	//获取信号编号
		} 
		else 
		{
			printf("other...\n");
		}
	}
	return 0;
}
4.waitpid函数
函数作用:
 作用同wait,但可指定pid进程清理,可以不阻塞。
 头文件:
   #include <sys/types.h>
   #include <sys/wait.h>
 函数原型:
   pid_t waitpid(pid_t pid, int *status, in options); 
 函数参数:
参数pid: 
	> 0  回收指定ID的子进程,回收指定子进程时,只要该子进程结束,就可回收,回收时间>=子进程结束时间。
	-1   回收任意子进程(相当于wait)
	0    回收和当前调用waitpid一个组的所有子进程
	<-1 回收指定进程组内的任意子进程    可以和kill命令一起使用,例如:kill -9 -进程组ID
参数options:
	0 :表示阻塞状态回收子进程
	WNOHANG:表示非阻塞状态回收子进程
返回值:
   成功:返回清理掉的子进程ID;
   失败:-1(无子进程)
 特殊返回值:
   当返回0:参数3为WNOHANG,表示子进程正在运行。
注意:
 一次wait或waitpid调用只能清理一个子进程,清理多个子进程应使用循环。 
 ps ajx #可以查看进程组ID
例子-阻塞回收一个进程:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
	int n = 3, i;				
    pid_t p, q = -1;
	//默认创建3个子进程
	for(i = 0; i < n; i++)	 
    {
        //创建子进程
        p = fork();
		if (i == 1)
        {
            //获得子进程为2的进程id
            q = p;
        }
		if(p == 0) 
        {
			break;			//出口2,子进程出口,i不自增
        }
    }
	if(n == i)
    {
		sleep(n);
		waitpid(q, NULL, 0);//阻塞状态回收q子进程,也就是第2个子进程,其他子进程不回收
		printf("我是父进程, pid = %d,子进程 %d 已被回收!\n", getpid(),q);
        while(1);
	} 
    else {
		sleep(i);
		printf("我是第 %d 个子进程, pid = %d, 组pid=%d\n", 
				i+1, getpid(), getgid());
		if(i != 1)
		{
			//while(1);
		}
	}
	return 0;
}
例子-阻塞等待回收所有子进程:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
	int n = 5, i;				
    pid_t p, q;
    //默认创建5个子进程
	for(i = 0; i < n; i++)	 
    {
        p = fork();
		if (i == 3)
        {
            q = p;
        }
		if(p == 0) {
			break;			//子进程出口,i不自增
        }
    }
	if(n == i){
		sleep(n);
		while(waitpid(-1, NULL, 0) != -1);//阻塞状态回收全部子进程
		printf("已回收所有的子进程!\n");
	} 
    else 
    {
		sleep(i);
		printf("我是第%d个子进程, pid = %d, 组pid=%d\n", i+1, getpid(), getgid());
	}
	return 0;
}
例子-非阻塞等待回收子进程:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
int main(void)
{
	pid_t pid, pid2,wpid;
	int flg = 0;
	int k = 0;
	for (size_t i = 0; i < 2; i++)
	{
		pid = fork();//创建一个子进程
		k = i;
		if(pid == 0)
		{
			break;
		}
	}
	if(pid == -1)
    {
        //创建失败
		perror("fork error");
		exit(1);
	} 
    else if(pid == 0)
    {		
        //子进程
		printf("我是子进程, pid = %d\n", getpid());
		if(k == 0)
		{
			sleep(3);
		}
		else
		{
			sleep(8);
		}
		exit(2);//结束
	}
	else {					//父进程
		//等待回收子进程,会等待所有的子进程回收完才退出程序
		do {
			wpid = waitpid(-1, NULL, WNOHANG);//获取进程状态,wpid>0,表示回收了一个进程
			
			printf("---wpid = %d--------%d\n", wpid, flg++);//打印log
			if(wpid == 0)
            {
                //返回0表示有子进程正在运行,没有结束
				printf("NO child exited\n");
				sleep(1);		
			}
			else
            {
				k--;
			}
		} while (k >= 0);		//子进程不可回收
		//进程全部回收完毕
		printf("我是父进程,pid = %d,所有子进程回收完毕!\n", wpid);
	}
	return 0;
}
以上就是本次的分享了,希望能对广大网友有所帮助。
此博主在CSDN发布的文章目录:【我的CSDN目录,作为博主在CSDN上发布的文章类型导读】













![[Python工匠]输出③容器类型](https://img-blog.csdnimg.cn/26100ccda1774af5839e97f6ac5f5047.png)





