当前位置: 首页 > ds >正文

【Linux进程控制一】进程的终止和等待

【Linux进程控制一】进程的终止和等待

  • 一、进程终止
    • 1.main函数的return
    • 2.strerror函数
    • 3.库函数exit
    • 4.系统调用_exit和库函数exit的区别
    • 5.异常信号
    • 6.变量errno
  • 二、进程等待
    • 1.什么是进程等待?
    • 2.wait接口
    • 3.status
    • 4.waitpid接口

一、进程终止

1.main函数的return

写C/C++代码时总会在写了int main函数的结尾写return 0,但是程序肯定不止能return 0

1.main函数和非main函数执行到return语句时表示此函数执行完毕
2.程序正常执行完毕且结果正确时return返回0
3.程序正常执行完毕但结果不正确时return返回非0

非0值有很多,1、2、3、4等数字分别有什么含义呢?

2.strerror函数

在这里插入图片描述
作用:将错误码转换为错误字符串
在这里插入图片描述

在这里插入图片描述
0对应成功,且在134号错误之后都是未知错误
在这里插入图片描述
查看最近进程的退出码: 指令echo $?
测试echo $?指令:
在这里插入图片描述

在这里插入图片描述

所以return返回的就是错误码

3.库函数exit

在这里插入图片描述

exit的参数即为错误码,和main函数return的值一样

exit函数和return的区别:
1.return只有在main中使用时才能代表此进程退出
2.exit函数在程序任一地方使用都可以直接退出进程,并且返回错误码
测试一下:
在这里插入图片描述

在这里插入图片描述
使用echo $?打印出来的退出码是11

4.系统调用_exit和库函数exit的区别

man的2号手册可以看见_exit系统调用:
它和exit一样都是终止进程且_exit的参数也代表错误码
在这里插入图片描述
两个有什么区别?用两段代码来验证:

//第一次运行的代码:printf("打印成功!");exit(22);
//第二次运行的代码:printf("打印成功!");_exit(22);

在这里插入图片描述

第一次打印了文字,第二次没有打印

printf打印本质是行刷新,如果不使用\n换行数据就一直存储在缓冲区不会打印
当用库函数exit结束进程就能把缓冲区的数据打印出来(刷新缓冲区数据)
而系统调用_exit就不会刷新缓冲区数据

深入思考:
exit是C语言提供的库函数,而_exit是操作系统提供的系统调用,_exit无法刷新缓冲区就说明缓冲区不在操作系统内,也就是说缓冲区不由操作系统来维护,而是由C标准维护

5.异常信号

对于任意一个进程,都只有两种退出情况:
1.代码执行完毕,正常退出
2.进程发生了异常,被迫退出

命令行输入Ctrl+C传递信号杀掉进程是异常终止
比如说访问空指针,野指针,除零错误等等,这些都会导致进程直接终止

在这里插入图片描述
在这里插入图片描述
报出一个错误Floating point exception,说明可能存在 / 或者 % 的除数为 0 的情况,这就说明进程发生了异常
进程发生异常的本质,是收到了异常信号
通过指令kill -l来查看异常信号:
在这里插入图片描述
8号正是singal Floating point exception的缩写
在这里插入图片描述
该程序会陷入死循环,然后每隔一秒输出自己的pid
当进程执行起来后我们对其发送8信号
在这里插入图片描述
左侧窗口执行进程后,pid=559712,右侧窗口执行指令kill -8 559712,就向559712发送了8号信号,此时进程直接退出,并输出Floating point exception
明明是一个死循环,我们可以通过发送异常信号直接终止,而代码中明明没有除零错误,我们也可以通过信号对其强行加上异常

程序异常崩溃时,return的退出码是无意义的,因为退出码对应的return语句还没执行就已经崩溃了

6.变量errno

errno是C语言中的一个全局变量,它里面存储的是上一次函数调用的错误码,比如使用fopen函数打开文件时,如果打开失败不仅文件指针fp会被赋值为NULL,同时错误码errno也会被系统自动赋值
在这里插入图片描述
在这里插入图片描述
在当前目录下,不存在一个叫做111.c的文件,我们用fopen以r形式打开,那么fopen是打不开文件的,于是就会向errno进行写入
在这里插入图片描述
执行结果:输出了错误码2,strerror将错误码转换为错误字符串:即没有对应的文件或目录

二、进程等待

1.什么是进程等待?

进程等待用于回收子进程的资源,避免子进程的PCB一直占用资源,并且可以获取子进程的退出信息,得知子进程任务的执行情况,进程等待主要通过两个系统调用接口waitwaitpid来完成

2.wait接口

使用wait接口,需要包含头文件<sys/types.h><sys/wait.h>,其函数原型为:

pid_t wait(int* stat_loc);

wait的返回值是一个int类型
返回值大于0,返回等待到的子进程的pid
返回值小于0,等待失败
在这里插入图片描述
先通过fork创建了一个子进程
子进程进入if语句,进行五秒倒计时,然后退出,并且退出码为5
父进程则通过wait函数进行等待,传入指针&status,接收返回值pid,最后输出status和pid的值
在这里插入图片描述

  1. wait的返回值就是子进程的pid
  2. status一开始被初始化为0,wait之后,status = 1280,可知wait确实会修改传入的参数
  3. 子进程总共sleep了五秒,而父进程在等待的这五秒中,没有任何动作,就等着子进程结束,然后对它进行回收,这个过程父进程处于阻塞状态,称为阻塞等待

status为什么是1280?

3.status

status要当作一个位图来看:
在这里插入图片描述

  • 灰色部分:(status是一个int类型,占32比特)后16比特是无效的,不填入任何内容
  • 黄色部分:第8 - 15位,共8比特,表示wait到的子进程的退出码
  • 绿色部分:第7位,core dump标志位本章节不考虑该位置
  • 蓝色部分:第0 - 6位,共7比特,用户表示wait到的子进程的退出信号

因为return的退出码是5,二进制为0101,所以status后16位是0000 0101 0000 0000,转换为十进制正是wait修改后的status值1280,退出无异常所以退出信号为0

从status中提取出退出码和退出信号,就要对其进行位操作:

status直接与01111111进行按位与&,就能得到退出信号,01111111的十六进制表示为0X7F
int sig = status & 0x7F;

status右移8位后,与11111111进行按位与&,就能得到退出码,11111111的十六进制表示为0XFF
int code = (status >> 8) & 0xFF;

在这里插入图片描述
在这里插入图片描述
可以看到退出码为5,退出信号为0
Linux还给用户提供了两个宏函数,用于检测status:

WIFEXITED:检测进程是否正常退出,返回一个布尔值,如果进从正常退出,返回真
WEXITSTATUS:提取子进程的退出码,也就是第8 - 15位
使用:

if(WIFSIGNALED(status))printf("exit code = %d\n", WEXITSTATUS(status));
elseprintf("子进程退出异常...\n");

4.waitpid接口

进程等待的另外一个接口是waitpid接口,需要包含头文件<sys/types.h><sys/wait.h>,其函数原型为:

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

一个进程是可以有多个子进程的,但一个wait只能等待一个子进程,如果有多个子进程,那么wait函数只能等待第一个结束的子进程,而waitpid则是针对pid来对进程进行等待

waitpid的第一个参数是子进程的pid,第二个参数用于接收退出信息也就是刚刚的status,第三个参数用于控制等待的模式

通过代码验证wait和waitpid的区别:
在这里插入图片描述
我们通过fork创建了两个子进程,第一个子进程输出自己的pid后会sleep五秒,而第二个子进程输出pid后sleep一秒
父进程只wait一次,最后父进程输出wait的返回值,而返回值就是等待到的子进程的pid,这样就可以判断父进程wait到了哪一个子进程
在这里插入图片描述
child1的pid = 624488,child2的pid = 624489,而wait的返回值为624489,说明wait到了第二个进程。因为第二个进程先结束,所以被wait先接收了

现在我们把上述代码中的wait改为waitpid再次尝试:
在这里插入图片描述
通过waitpid的第一个参数,指定等待id1,也就是第一个子进程,第三个参数先设为0,后续讲解该参数的作用
在这里插入图片描述
返回值和child1匹配上了,可以说明虽然child1更晚结束,但是waitpid只会等待指定的进程,就算有其他子进程先结束了waitpid也不会去先回收

第三个参数用于控制进程等待的模式:

0:进行阻塞等待
WNOHANG:进行非阻塞等待

讲到wait时,简单提到了阻塞等待,也就是父进程在wait的时候,什么也不做,进入阻塞状态,直到wait成功

而非阻塞等待不同,进行非阻塞等待时,如果本次waitpid暂时没有等待到,那么父进程不会阻塞,waitpid直接返回0,表示本次等待没有等待到子进程。此时父进程就可以空出时间去完成别的任务,而不是什么也不做一直在等了

代码验证:
在这里插入图片描述
先通过fork创建了一个子进程,子进程sleep五秒。父进程陷入一个while死循环,每次循环开始,都以WNOHANG模式waitpid一次,由于该模式不会阻塞等待,只要当前子进程没有结束,那么waitpid直接返回0,去执行后面的if语句
如果当前返回值为0,说明当前子进程没有结束
如果当前返回值> 0,说明子进程结束了,waitpid也成功了,此时返回值就是子进程的pid ,跳出循环
在这里插入图片描述
子进程一共执行五秒后才退出,以非阻塞等待的模式,父进程就可以把这五秒拿去做其他事情

http://www.xdnf.cn/news/5260.html

相关文章:

  • CDGP重点知识梳理
  • dropout层
  • [C++类和对象]类和对象的引入
  • 计算机的基本组成
  • 最优化方法Python计算:有约束优化应用——线性Lasso回归分类器
  • 最新CDGP单选题(第一章)补充
  • Etcd 数据存储文件
  • 单片机开发基础与高效流程
  • ECS在游戏服务器中的应用:Java实现与最佳实践
  • SpringAI框架中的RAG知识库检索与增强生成模型详解
  • CNN处理图片
  • 关于 OpenGL 的上下文、线程和共享上下文之间的关系
  • Dive into LVGL (1) —— How LVGL works from top to down
  • 期货反向跟单—数据分析误区(二)盘手排名
  • 60分钟示范课设计-《Python循环语句的奥秘与应用进阶》
  • 第J7周:对于ResNeXt-50算法的思考
  • 网上商城系统
  • 【嵌入式系统设计师(软考中级)】第二章:嵌入式系统硬件基础知识——⑤电源及电路设计
  • 全国青少年信息素养大赛 Python编程挑战赛初赛 内部集训模拟试卷四及详细答案解析
  • 解决librechat 前端界面没有google gemini 2.5模型的选项
  • 【c语言】动态内存管理
  • 各种注解含义及使用
  • 心 光 -中小企实战运营和营销工作室博客
  • 微机控制高温扭转试验机
  • 关于AI 大数据模型的基础知识 杂记
  • 数字化与信息化的关系
  • 4.3 Thymeleaf案例演示:图书管理
  • 军事目标无人机视角坦克检测数据集VOC+YOLO格式4003张1类别
  • 44.辐射发射整改简易摸底测试方法
  • 企业名录搜索软件哪家好?