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

发布于:2025-05-10 ⋅ 阅读:(15) ⋅ 点赞:(0)

一、进程终止

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));
else
    printf("子进程退出异常...\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 ,跳出循环
在这里插入图片描述
子进程一共执行五秒后才退出,以非阻塞等待的模式,父进程就可以把这五秒拿去做其他事情


网站公告

今日签到

点亮在社区的每一天
去签到