父进程等待子进程退出 / 僵尸进程孤儿进程

news2025/1/13 3:18:44

Q:父进程为什么要等待子进程退出?

A:回顾创建子进程的目的,就是让子进程去处理一些事情,那么“事情干完了没有”这件事,父进程需要知道并收集子进程的退出状态子进程的退出状态如果不被收集,就会变成僵尸进程而如果父进程在子进程之前就退出了,则此时的子进程会变成孤儿进程。

而父进程会通过下面几个宏来解析具体返回的状态码:

 

僵尸进程

其实上上节的demo2的代码就会产生僵尸进程,因为父进程没有收集子进程的退出状态:

demo2.c:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>


int main()
{
	pid_t pid;
	pid_t fork_return;
	int cnt = 0;

	pid = getpid();
	printf("before fork, PID = %d\n",pid);

	fork_return = vfork();

	if(fork_return > 0){
		while(1){
			printf("This is the father JC,PID = %d\n",getpid());
			sleep(2);
		}
	}else{
		while(1){
			printf("This is the son JC,PID = %d\n",getpid());
			sleep(2);
			cnt++;
			if(cnt == 3){
				exit(-1);
			}
		}
	}


	return 0;
}

运行效果:

看起来似乎运行效果很对,但如果使用"ps -aux|grep zombie"查看进程就会发现,PID号为3126的子进程已经变成了僵尸进程

“ S+ ”代表 进程正在正常运行中

“ Z+ ”代表 僵尸进程

孤儿进程 

Linux为了避免系统存在过多孤儿进程,init进程收留孤儿进程,变成孤儿进程的父进程。

修改demo2.c:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>


int main()
{
	pid_t pid;
	pid_t fork_return;
	int cnt;

	pid = getpid();
	printf("before fork, PID = %d\n",pid);

	fork_return = fork();

	if(fork_return > 0){
		printf("This is the father JC,PID = %d\n",getpid());
	}else{
		while(1){
			printf("This is the son JC,PID = %d, my father JS's PID = %d\n",getpid(),getppid());
			sleep(2);
			cnt++;
			if(cnt == 3){
		        	exit(1);
			}
		}
	}


	return 0;
}

运行效果:

可见,父亲打印一条消息就会去世,在去世前,子进程的父亲就是原来程序的PID,但是当父亲离开后,子进程被PID为1797的进程收养了

通过“ps -aux” 查找1797:

但是根据概念,子进程不应该被PID号为1的进程收养吗?原因看这里:

父进程终止,子进程未被init收养问题_抱走♡的博客-CSDN博客 

所以是Linux的系统版本导致的问题应该= =

 

wait相关函数 

需要添加的库:

#include <sys/types.h>
#include <sys/wait.h>

wait函数原型:

 pid_t wait(int *wstatus);

参数说明1:

  • wstatus:这是一个整数型指针,如果设置为“NULL”,则表示不关心退出的状态如果不设置为“NULL”,则子进程退出的状态会放在这个指针指向的地址中

 

waitpid函数原型: 

waitpid和wait的区别就是,wait函数调用后在子进程退出前父进程会被强制阻塞而waitpid中有一个参数可以使得父进程不被阻塞。

pid_t waitpid(pid_t pid, int *wstatus, int options);

参数说明2:

  • pid:

  • wstatus:这是一个整数型指针,如果设置为“NULL”,则表示不关心退出的状态如果不设置为“NULL”,则子进程退出的状态会放在这个指针指向的地址中
  • options:

  1.  option如果设置为“WNOHANG”,则 若由PID指定的子进程不是立刻可用的,则waitpid不阻塞,此时其返回值为0
  2.  option如果设置为“WUNTRACED”,则 若某实现支持作业控制,而由PID指定的任一子进程已处于暂停状态,并且其状态自暂停以来还未报告过,则返回其状态,WIFSTOPPED宏确定返回值是否对应于一个暂停子进程
  3.  option如果设置为“WCONTINUED”,则 若实现支持作业控制,那么由PID指定的任一子进程在暂停后已经继续,但其状态尚未报告,则返回其状态(POSIX.1的XSI拓展)

 

父进程等待退出并收集状态的演示

demo3.c:

使用wait函数,并将wstatus设置为NULL:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
	pid_t pid;
	pid_t fork_return;
	int cnt = 0;

	pid = getpid();
	printf("before fork, PID = %d\n",pid);

	fork_return = fork();

	if(fork_return > 0){
		wait(NULL);
		while(1){
			printf("This is the father JC,PID = %d\n",getpid());
			sleep(2);
		}
	}else{
		while(1){
			printf("This is the son JC,PID = %d\n",getpid());
			sleep(2);
			cnt++;
			if(cnt == 3){
				exit(1);
			}
		}
	}


	return 0;
}

实现效果1:

 

可见虽然使用的是fork函数而不是vfork,但是由于父进程调用了wait函数,所以在子进程运行时一直阻塞,直到子进程退出,父进程才开始执行。

使用"ps -aux|grep demo3-1"查看进程:

可见,此时PID为3056的子进程已经完全退出,所以没有之前出现的僵尸进程了。 

使用wait函数,并不将wstatus设为NULL:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
	pid_t pid;
	pid_t fork_return;
	int cnt = 0;
	int status = 0;

	pid = getpid();
	printf("before fork, PID = %d\n",pid);

	fork_return = fork();

	if(fork_return > 0){
		wait(&status);
		printf("child quit, exit status = %d\n",WIFEXITED(status));
		while(1){
			printf("This is the father JC,PID = %d\n",getpid());
			sleep(2);
		}
	}else{
		while(1){
			printf("This is the son JC,PID = %d\n",getpid());
			sleep(2);
			cnt++;
			if(cnt == 3){
				exit(1);
			}
		}
	}


	return 0;
}

注意,由于此时的子进程是正常退出则刚刚提到的宏“WIFEXITED”的值为真,并且可以使用 “WEXITSTATUS” 来解析状态,才可以得到正确的值

实现效果2:

可见,此时在子进程正常退出后,父进程在运行前还得到了子进程退出时的状态码

使用"ps -aux|grep demo3-2"查看进程:

可见,PID号为3109的子进程已经退出

 

demo4.c:

使用waitpid函数,并将option设为“WNOHANG”:

waitpid(fork_return,&status,WNOHANG);

回顾刚刚讲的PID参数如果>0,则等待“进程号等于这个PID”的子进程,而之前就说过fork的返回值就是子进程的PID,所以在这里直接将第一个参数设置为fork_return

实现效果:

可见,这次父进程没有阻塞并且直接返回,然后父子进程开始抢占CPU,等子进程成功执行三次退出之后,再次变成只有父进程在执行了。

 但是此时,使用"ps -aux|grep a.out"查看进程:

可见: PID号为3254的子进程变成了一个僵尸进程

所以,父进程的非阻塞等待会造成子进程变成僵尸进程!

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/870100.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

系统架构设计专业技能 · 系统安全分析与设计(四)【加解密、数字信封、信息摘要、数字签名、数字书证、网络安全、信息安全】

系列文章目录 系统架构设计专业技能 网络规划与设计&#xff08;三&#xff09;【系统架构设计师】 系统架构设计专业技能 系统安全分析与设计&#xff08;四&#xff09;【系统架构设计师】 系统架构设计高级技能 软件架构设计&#xff08;一&#xff09;【系统架构设计师…

SQL 语句解析过程详解

SQL 语句解析过程详解&#xff1a; 1&#xff0e;输入SQL语句 2&#xff0e;词法分析------flex 使用词法分析器&#xff08;由Flex生成&#xff09;将 SQL 语句分解为一个个单词&#xff0c;这些单词被称为“标记“。标记包括关键字、标识符、运算符、分隔符等。 2.1 flex 原…

CCLINK转MODBUS-TCP网关cclink通讯接线图 终端电阻

大家好&#xff0c;今天我们要聊的是生产管理系统中的CCLINK和MODBUS-TCP协议&#xff0c;它们的不同使得数据互通比较困难&#xff0c;但捷米JM-CCLK-TCP网关的出现改变了这一切。 1捷米JM-CCLK-TCP是一款自主研发的CCLINK从站功能的通讯网关&#xff0c;它的主要功能是将各种…

JavaWeb 中对 HTTP 协议的学习

HTTP1 Web概述1.1 Web和JavaWeb的概念1.2 JavaWeb技术栈1.2.1 B/S架构1.2.2 静态资源1.2.3 动态资源1.2.4 数据库1.2.5 HTTP协议1.2.6 Web服务器 1.3 Web核心 2 HTTP2.1 简介2.2 请求数据格式2.2.1 格式介绍2.2.2 实例演示 2.3 响应数据格式2.3.1 格式介绍2.3.2 响应状态码2.3.…

linux中的ifconfig和ip addr

在linux操作系统中ifconfig和ip addr都是显示网卡配置信息的命令&#xff0c;好多人有疑惑它们有什么区别呢 区别1&#xff1a;对于linux发行的版本不一样 ip addr是对新发行版本的linux使用会比较多&#xff1b;而ifconfig是老版本遇到使用的会比较多。 区别2&#xff1a;显…

mysql8和mysql5的安装过程都有!!!超多图超详细保姆级教程最新教程新手小白轻松上手,带你了解清楚你安装过程的每一个术语

目录 前言mysql5和mysql8的区别1.官网下载2.mysql8的安装2.1安装程序打开前2.2Choosing a Setup Type选择安装模式2.3Select Products选择组件2.3.1Select Products的组件解释2.3.2Select Products的组件选择2.3.3电脑操作系统位数查看2.3.4Select Products的组件的内容配置2.3…

锐捷设备密码管理、密码恢复、恢复出厂设置

目录 配置登录用户名密码以及Enable密码 只需要密码登录 需要用户名和密码登录&#xff08;无AAA&#xff09; 需要用户名和密码登录&#xff08;有AAA&#xff09; 密码恢复 Web密码忘记 Telnet/SSH密码忘记 Console密码忘记 所有密码都忘记&#xff0c;通过Console进…

微信小程序在使用vant组件库时构建npm报错

在跟着vant官方进行使用步骤一步步操作时&#xff0c;由于要构建NPM&#xff0c;但NPM包在App配置文件的外部 所以在做下图这一步时&#xff1a; 接着再进行npm构建时会报错 message:发生错误 Error: F:\前端学习\前端框架\小程序\project\demo\miniprogram解决方法 &#xf…

Offset Explorer

Offset Explorer 简介下载安装 简介 Offset Explorer&#xff08;以前称为Kafka Tool&#xff09;是一个用于管理和使Apache Kafka 集群的GUI应用程序。它提供了一个直观的UI&#xff0c;允许人们快速查看Kafka集群中的对象以及存储在集群主题中的消息。它包含面向开发人员和管…

1、深入理解Mysql索引底层数据结构与算法(前置课程)

1.表中数据看似挨在一起&#xff0c;本质写在磁盘里&#xff0c;随着时间和其他程序占用可能分散分布 k为值 v为磁盘空间地址 2.为什么mysql选择B树 假如b树存放2k万数据 一个节点16KB&#xff0c;里面带有data的话存的太少&#xff0c;导致树的高度过高&#xff0c;所以让…

配置:Terminal和oh-my-posh

目录 命令行安装oh-my-posh查看安装情况配置PowerShell启用oh-my-posh、设置主题配色安装字体Terminal中的配置 命令行安装oh-my-posh Set-ExecutionPolicy Bypass -Scope Process -Force; Invoke-Expression ((New-Object System.Net.WebClient).DownloadString(https://ohmy…

Java四大查找(二)插值查找

Java四大查找 一. 插值查找1.1 题目1.2 思路分析&#xff08;图解加文字&#xff09;1.3 代码演示 一. 插值查找 1.1 题目 给定一个有序数组&#xff0c;在有序数组中找到该数字。如果有&#xff0c;则返回该数字的下标&#xff0c;如果没有&#xff0c;则返回-1. 1.2 思路分析…

模拟IIC——关于模拟IIC的IO口的配置选取推挽输出还是开漏输出,以及是否需要更改IO口输入输出模式和是否需要对IO配置上拉

问题如下 当时我以为引脚配错了&#xff0c;原理图明明是B引脚&#xff0c;为何程序是C呢 查了一下资料&#xff0c;顿悟了 https://blog.csdn.net/m0_62243928/article/details/125779308 在使用模拟IIC的时候&#xff0c;观看别人的程序的时候发现了程序之间的一些不一…

RabbitMQ工作流程详解

1 生产者发送消息的流程 (1)生产者连接RabbitMQ&#xff0c;建立TCP连接(Connection)&#xff0c;开启信道(Channel) (2)生产者声明一个Exchange (交换器)&#xff0c;并设置相关属性&#xff0c;比如交换器类型、是否持久化等 (3)生产者声明一个队列井设置相关属性&#xf…

2023国赛数学建模B题思路分析

文章目录 0 赛题思路1 竞赛信息2 竞赛时间3 建模常见问题类型3.1 分类问题3.2 优化问题3.3 预测问题3.4 评价问题 4 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 竞赛信息 全国大学生数学建模…

2023国赛数学建模A题思路分析

文章目录 0 赛题思路1 竞赛信息2 竞赛时间3 建模常见问题类型3.1 分类问题3.2 优化问题3.3 预测问题3.4 评价问题 4 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 竞赛信息 全国大学生数学建模…

安卓:网络框架okhttp

目录 一、okhttp介绍 1. OkHttpClient类&#xff1a; 常用方法&#xff1a; 2. Request类&#xff1a; 常用方法&#xff1a; 3. Response类&#xff1a; 常用方法&#xff1a; 4. Call类&#xff1a; 常用方法&#xff1a; 5. Interceptor接口&#xff1a; 常用方法&…

Web 服务器 -【Tomcat】的简单学习

Tomcat1 简介1.1 什么是Web服务器 2 基本使用2.1 下载2.2 安装2.3 卸载2.4 启动2.5 关闭2.6 配置2.7 部署 3 Maven创建Web项目3.1 Web项目结构3.2 创建Maven Web项目 4 IDEA使用Tomcat4.1 集成本地Tomcat4.2 Tomcat Maven插件 Tomcat 1 简介 1.1 什么是Web服务器 Web服务器是…

【MySQL】并发执行事务可能存在的问题, 事务的四种隔离级别

文章目录 前言一、并发执行事务可能存在的问题1, 脏读问题2, 不可重复读3, 幻读 二、MySQL 的四种隔离级别1, READ UNCOMMITTED 读未提交2, READ COMMITTED 读已提交3, REPEATABLE READ 可重复读 (MySQL 的默认事务隔离级别)4, SERIALIZABLE 串行化 总结 前言 各位读者好, 我是…

Mac如何打开隐藏文件中Redis的配置文件redis.conf

Redis下载(通过⬇️博客下载的Redis默认路径为&#xff1a;/usr/local/etc) Redis下载 1.打开终端进入/usr文件夹 cd /usr 2.打开/local/文件夹 open local 3.找到redis.conf并打开,即可修改配置信息