Linux系统进程——进程的退出、子进程退出的收集、孤儿进程

进程退出

进程退出主要分为两种:正常退出、异常退出

正常退出

正常退出分为以下几种:

1.main函数调用return
2.进程调用exit(),标准c库
3.进程调用 _exit() 或者 _Exit() ,属于系统调用
4.进程最后一个线程返回
5.最后一个线程调用pthread exit(4和5与线程有关)

异常退出

异常退出分为以下几种:

1.调用abort函数
2.当进程收到某些信号时,如 ctrl+C
3最后一个线程对取消 (cancellation) 请求做出响应

退出函数的解析

退出函数分别有exit()、_exit()、_Exit(),其函数原型和参数如下:

函数原型所包含头文件参数
void exit(int status) <stdlib.h>exit(0)正常退出;exit(1)异常退出
void _exit(int status) <unistd.h>_exit(0)正常退出;_exit(1)异常退出
void _Exit(int status)<stdlib.h>

_Exit(0)正常退出;_Exit(1)异常退出

status 是一个整型的参数,可以利用这个参数传递进程结束时的状态。一般来说,0表示正常结束;其它的值表示出现了错误,进程非正常结束在实际编程时,可以用 wait()系统调用接收子述程的返回值,针对不同的情况进行不同的处理

第二种与第三种类似,但第一种与其他两种有一定的区别,区别如下:

从图里可以看出来,_exit()函数的作用是: 直接使进程停止运行,清除其使用的内存空间,并清除其在内核中的各种数据结构; 而exit()函数则在这 些基础上做了一些包装,在执行退出之前加了若干道工序. exit()函数和_exit()函数的最大区别就在于exit()函数在终止当前进程之前要检查该进程 打开过那些文件,把文件缓冲区中的内容写回文件,也就是图中的 "清楚I/O缓冲" 一项.

不管进程如何终止,最后都会执行内核中的同一段代码。这段代码为相应进程关闭所有打开描述符,释放它所使用的存储器等。对上述任意一种终止情形、我们都希望终止进程能够通知其父进程它是如何终止的。对于三个终止函数(exit._exit和_Exit),实现这一点的方法是,将其退出状态((exit status)作为参数传送给函数。在异常终止情况下,内核(不是进程本身)产生一个指示其异常终止原因的终止状态(termination status)。在任意一种情况下,该终止进程的父进程都能用wait或waitpid函数取得其终止状态。

子进程退出的收集

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

父进程得管理子进程,所以父进程派给子进程的任务完成的如何,我们都需要知道,如,子进程运行完成,运行结果对还是不对,或者是否正常退出 、父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息

不收集子进程状态会发生什么?

子进程状态不被收集会变成僵尸进程,造成内存泄漏

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

int main()
{
	int vfork_t = 0;
	int count = 0;

	vfork_t = vfork();

	if(vfork_t > 0)
	{
		while(1)		
		{	
			printf("count = %d\n",count);
			printf("This is father,pid =%d \n",getpid());
			sleep(1);
		}
	}
	else if(vfork_t == 0)
	{
		while(1)
		{
			printf("This is child,pid =%d \n",getpid());
			sleep(1);
			count++;
			if(count == 3)
			{
				exit(0);
			}
		}
				
	}
	return 0;
}

可见父进程显示S+(正常运行),而子进程显示Z+(僵尸体(死)进程)

收集子进程的函数调用

收集子进程可以调用的函数有wait(),waitpid(), waitid()

waiit()

包含的头文件

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

函数原型

pid_t wait(int *status);

参数解读

status参数:

一个整型数指针
非空:子进程退出状态放在它所指向的地址中
空:不关心退出状态

返回值

wait():如果成功,返回终止子进程的进程ID;错误,返回-1。

针对wait函数的参数做出试验

1.空:wait(NULL)

将上面函数vfork换成fork并调用wait函数,如下:

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

int main()
{
	int fork_t = 0;
	int count = 0;

	fork_t = fork();

	if(fork_t > 0)
	{
		wait(NULL);
		while(1)		
		{	
			printf("count = %d\n",count);
			printf("This is father,pid =%d \n",getpid());
			sleep(1);
		}
	}
	else if(fork_t == 0)
	{
		while(1)
		{
			printf("This is child,pid =%d \n",getpid());
			sleep(1);
			count++;
			if(count == 3)
			{
				exit(0);
			}
		}
				
	}
	return 0;
}

可见,在子进程运行三次后,收集子进程,轮到父进程运行,此时运行程序没有子进程,即子进程不会变成僵尸进程,而父进程正常运行

2.非空:wait(*status)

将上面函数的NULL改成定义的任意值,并通过观察该只判断子进程状态

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

int main()
{
	int fork_t = 0;
	int count = 0;
	int status = 9;

	fork_t = fork();

	if(fork_t > 0)
	{
		wait(&status);//观察子进程状态
		printf("child quit,status is %d\n",WEXITSTATUS(status));//打印子进程状态,值应为exit括号里的返回值
		while(1)		
		{	
			printf("count = %d\n",count);
			printf("This is father,pid =%d \n",getpid());
			sleep(1);
		}
	}
	else if(fork_t == 0)
	{
		while(1)
		{
			printf("This is child,pid =%d \n",getpid());
			sleep(1);
			count++;
			if(count == 3)
			{
				exit(3);
			}
		}
				
	}
	return 0;
}

可见,当指针为非空时,子进程的退出状态会保存在指向的地址中,即exit函数括号里的值会指向status的地址,从而打印判断子进程的状态。

waitpid()

 包含的头文件

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

函数原型

pid_t waitpid(pid_t pid, int *status, int options);
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

参数解读

pid参数:

pid的值功能
pid<-1等待进程组识别码为 pid 绝对值的任何子进程
pid=-1等待任何子进程,相当于 wait()
pid=0等待进程组识别码与目前进程相同的任何子进程
pid>0等待任何子进程识别码为 pid 的子进程

status参数:

一个整型数指针
非空:子进程退出状态放在它所指向的地址中
空:不关心退出状态

options参数:

WCONTINUEL若实现支持作业控制。那么由pid指定的任一子进程在暂停后已经继续,但其状态尚未报告,则返回其状态(POSIX.1的XSI护展)

WNOHANG

     (常用)

若由pid指定的子进程并不是立即可用的,则waitpid不阻塞,此时其返回值为0
WUNTRACED若某实现支持作业控制,而由pid指定的任一子进程已处于暂停状态,并且其状态自暂停以来还未报告过,则返回其状态。WIFSTOPPED宏确定返回值是否对应于一个暂停子进程
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
	int fork_t = 0;
	int count = 0;
	int status = 9;

	fork_t = fork();

	if(fork_t > 0)
	{
		waitpid(fork_t,&status,WNOHANG);//第一个参数:父进程的返回值大于0,同时该返回值就是子进程的pid,而这里的参数就是子进程pid的值
		printf("child quit,status is %d\n",WEXITSTATUS(status));
		while(1)		
		{	
			printf("count = %d\n",count);
			printf("This is father,pid =%d \n",getpid());
			sleep(1);
		}
	}
	else if(fork_t == 0)
	{
		while(1)
		{
			printf("This is child,pid =%d \n",getpid());
			sleep(1);
			count++;
			if(count == 3)
			{
				exit(3);
			}
		}
				
	}
	return 0;
}

调用该函数后,子进程和父进程会一起运行,即父进程不会挂起,在运行3次之后,只有父进程运行,同时子进程此时表现为僵尸进程。

孤儿进程

父进程如果不等待子进程退出,在子进程之前就结束了自己的“生命”,此时子进程叫做孤儿进程。 Linux避免系统存在过多孤儿进程,==init进程(系统初始化进程,进程ID为1)==收留孤儿进程,变成孤儿进程的父进程。

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

int main()
{
	int fork_t = 0;
	int count = 0;
	int status = 9;

	fork_t = fork();

	if(fork_t > 0)
	{
		printf("This is fther pid,pid is  %d\n",getpid());//打印一次父进程的pid
	}
	else if(fork_t == 0)
	{
		while(1)
		{
			printf("This is child,pid =%d,my father is %d\n",getpid(),getppid());
			sleep(1);
			count++;
			if(count == 5)
			{
				exit(3);
			}
		}
				
	}
	return 0;
}

可见,父进程打印一次,打印完之后结束,此时只剩下子进程,不难发现,子进程的父进程pid值为1,相当于系统分配了个pid值为1的父进程收留这些孤儿进程;同时子进程还在运行,不为僵尸进程。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

晨控CK-FR08读卡器与汇川PLC连接EtherCAT通讯手册

晨控CK-FR08读卡器与汇川PLC连接EtherCAT通讯手册 晨控CK-FR08系列是一款基于射频识别技术的高频RFID标签读卡器&#xff0c;读卡器工作频率为13.56MHZ&#xff0c;支持对I-CODE 2、I-CODE SLI等符合ISO15693国际标准协议格式标签的读取。 读卡器同时支持标准工业通讯协议Eth…

ubuntu20源码编译搭建SRS流媒体服务器

第一、下载源码 下载源码&#xff0c;推荐用Ubuntu20&#xff1a; git clone -b develop https://gitee.com/ossrs/srs.git第二、编译 2.1、切换到srs/trunk目录&#xff1a; cd srs/trunk2.2、执行configure脚本 ./configure2.3、执行make命令 make2.4、修改conf/rtmp.c…

通讯录实现之进阶版将通讯录数据保存在文件中(完整代码)

我们在之前的博客中已经写过两版通讯录了&#xff1a; 第一版是用C语言实现了通讯录&#xff0c;但是通讯录的存储人数信息是固定的&#xff0c;用完就没有了 感兴趣的可以转到对应博客看一下&#xff0c;附带链接&#xff1a;第一版通讯录 第二版是在第一版的基础上动态开辟…

从零开始 通义千问大模型本地化到阿里云通义千问API调用

从零开始 通义千问大模型本地化到阿里云通义千问API调用 一、通义千问大模型介绍 何为“通义千问”&#xff1f; “通义千问大模型”是阿里云推出的一个超大规模的语言模型&#xff0c;具有强大的归纳和理解能力&#xff0c;可以处理各种自然语言处理任务&#xff0c;包括但…

DAC实验(DAC 输出三角波实验)(DAC 输出正弦波实验)

DAC 输出三角波实验 本实验我们来学习使用如何让 DAC 输出三角波&#xff0c;DAC 初始化部分还是用 DAC 输出实验 的&#xff0c;所以做本实验的前提是先学习 DAC 输出实验。 使用 DAC 输出三角波&#xff0c;通过 KEY0/KEY1 两个按键&#xff0c;控制 DAC1 的通道 1 输出两种…

文旅媒体有哪些?如何邀请到现场报道?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 中国文旅产业在近年来得到了持续而快速的发展。从产业端看&#xff0c;中国文旅产业呈现出新的发展趋势&#xff0c;其中“文旅”向“文旅”转变成为显著特点。通过产业升级和空间构建&a…

Shell编程基础(3)- Shell的位置参数

Shell编程基础&#xff08;3&#xff09;- Shell的位置参数 Shell Scripting Essentials (3) – Locative Parameters of Shell Scripting 前文介绍过shell变量。当声明shell变量时&#xff0c;只需要在代码行写出变量名称即可;在输入行用read命令要求用户输入&#xff0c;在…

Day48 力扣动态规划 : 647. 回文子串 |516.最长回文子序列 |动态规划总结篇

Day48 力扣动态规划 : 647. 回文子串 &#xff5c;516.最长回文子序列 &#xff5c;动态规划总结篇 647. 回文子串第一印象看完题解的思路dp递推公式初始化递归顺序 实现中的困难感悟代码 516.最长回文子序列第一印象我的尝试遇到的问题 看完题解的思路dp递推公式初始化 实现中…

设计基于STM32F103C8T6微控制器的巡线小车

巡线小车是一种能够在一条预定线追踪路径的小车&#xff0c;广泛应用于工业自动化、物流仓储、智能家居等领域。本设计将使用STM32F103C8T6微控制器来实现一个基础的巡线小车。 硬件组成&#xff1a;1. STM32F103C8T6微控制器开发板&#xff1a;作为巡线小车的核心控制器&…

双剑合璧:基于Elasticsearch的两路召回语义检索系统,实现关键字与语义的高效精准匹配

搜索推荐系统专栏简介:搜索推荐全流程讲解(召回粗排精排重排混排)、系统架构、常见问题、算法项目实战总结、技术细节以及项目实战(含码源) 专栏详细介绍:搜索推荐系统专栏简介:搜索推荐全流程讲解(召回粗排精排重排混排)、系统架构、常见问题、算法项目实战总结、技术…

NewStarCTF2023 Reverse Week3 EzDLL WP

分析 这里调用了z3h.dll中的encrypt函数。 用ida64载入z3h.dll 直接搜索encrypt 找到了一个XTEA加密。接着回去找key和密文。 发现key 这里用了个调试状态来判断是否正确&#xff0c;v71&#xff0c;要v7&#xff1d;1才会输出Right&#xff0c;即程序要处于飞调试状态。 可…

asp.net core EF Sqlserver

一、EF CORE的使用 1、使用NuGet来安装EF CORE 使用程序包管理器控制台&#xff0c;进行命令安装 //安装 Microsoft.EntityFrameworkCoreInstall-Package Microsoft.EntityFrameworkCore //安装 Microsoft.EntityFrameworkCore.SqlServer Install-Package Microsoft.EntityF…

Java智慧工地云SaaS源码,AI服务器、智能硬件

智慧工地智能硬件 一、自动喷淋控制 当扬尘监测值超过在智慧工地系统中设定的闽值后自动喷淋控制系统通过接收系统发出的开关指令&#xff0c;实现自动、及时喷淋降尘&#xff0c;同时系统可设置自动喷淋时间段&#xff0c;每天定时喷淋&#xff0c;避免环境污染。 二、智能电…

采用Nexus搭建Maven私服

采用Nexus搭建Maven私服 1.采用docker安装 1.创建数据目录挂载的目录&#xff1a; /usr/local/springcloud_1113/nexus3/nexus-data2.查询并拉取镜像docker search nexus3docker pull sonatype/nexus33.查看拉取的镜像docker images4.创建docker容器&#xff1a;可能出现启动…

【vue】下载导出excel

下载导出excel 首先使用的tdesign框架&#xff0c;要导出后端返回的数据流excel 遇见的问题 下载的文件&#xff0c;里边的内容是undefined 观察报错 一看就知道并不是后端的报错&#xff0c;后端不可能是undefined 在强烈的好奇心驱动下&#xff0c;看了下接口&#xff0…

Docker安装MinIO遇到的(汇总——持续更新中)

文章目录 Docker安装MinIO遇到的坑前言问题1&#xff1a;执行docker run报错Error response from daemon问题2&#xff1a;启动MinIO容器浏览器无法访问问题3&#xff1a;上传文件报错InvalidResponseException问题4&#xff1a;上传文件报错Connection refused最终的启动指令问…

【Electron】electron-builder打包失败问题记录

文章目录 yarn下载的包不支持require()winCodeSign-2.6.0.7z下载失败nsis-3.0.4.1.7z下载失败待补充... yarn下载的包不支持require() 报错内容&#xff1a; var stringWidth require(string-width)^ Error [ERR_REQUIRE_ESM]: require() of ES Module /stuff/node_modules/…

轮播图(多个一起轮播)

效果图 class MainActivity : Activity(), Runnable {private lateinit var viewPager: ViewPagerprivate lateinit var bannerAdapter: BannerAdapterprivate val images ArrayList<Int>() // 存储图片资源的列表private val handler Handler() // 用于定时发送消息…

Linux磁盘分区快速上手(讲解详细)

一、磁盘分区 在Linux中&#xff0c;磁盘是通过分区来使用的。分区是将一个硬盘划分成几个逻辑部分来使用&#xff0c;在每个分区中可以存储不同的文件系统。因此&#xff0c;在挂载磁盘之前&#xff0c;我们需要先对磁盘进行分区。磁盘分区的过程可以通过命令行工具或图形界面…

Unity中Shader矩阵的转置矩阵

文章目录 前言一、转置的表示二、转置矩阵三、转置矩阵的总结1、(A^T^)^T^ A2、(A B)^T^ A^T^ B^T^3、(kA)^T^ kA^T^ (k为实数)4、(AB)^T^ B^T^A^T^5、如果 A A^T^ 则称A为对称矩阵6、如果 AA^T^ I(单位矩阵)&#xff0c;则称 A 为正交矩阵&#xff0c;同时 A^T^ A^-1…