【Linux】进程结束和进程等待
进程的结束
退出码的认识
在我们学习C/C++的时候我们通常在进行写main函数时,main函数主体写完后通常会进行写一条语句 " return 0 " ,这里的这条语句到底是什么意思呢??
我们知道当在主函数中调用其他函数或者在其他函数中实现函数调用时,return 进行返回时是将return 后面的语句进行返回到调用该函数内,通过变量进行接收,main函数中return 后面的返回值其实就是退出码。
退出码的概念
退出码是用来标定程序执行结果是否正确。
进程退出的情况
- 进程跑完了,并且进程结果正确------------return 0
- 进程跑完了,但是进程的结果不正确------return !0
- 进程没有跑完中途挂掉了,此时退出码没有意义
查看进程码
命令:echo $?
查看最近一个进程运行的退出码
- 思考:为什么进行查询完test程序的退出码后,在进行查询进程的退出码后显示进程的退出码为0呢??
答案是进行查看进程退出码的命令也是一个进程,该进程正常结束,所以通过查询最近一条进程的退出码是0。
如何设定main函数的退出码??
当我们不关心程序程序的退出码时,直接返回0即可;当我们关心进程的退出码时,此时就需要返回特定的退出码。
特定的退出码一般代表特定的错误
这种通过打印退出码的方式对计算机是非常友好的,但是对人是非常不友好的,所以我们通常将退出码进行文字描述,进行文字描述既可以自定义进行实现,也可以通过使用系统中的映射关系。
利用库函数strerror进行打印所有错误码和错误码的描述(系统)
错误码进行自定义描述
进程中止的三种方式
- main函数的return
- exit:exit是C语言的库函数
- _exit:_exit 是系统调用
exit和return 的区别
exit不需要在main函数中即可达到main函数中return的效应,直接退出程序并将错误码进行保留。
exit和_exit的区别
当存在换行符时,在main函数中进行打印ret结果可以进行呈现出来
当不存在换行符时,通过exit进行中止的进程ret还是可以被打印出来,但是_exit进行中止进程的ret没有显现
产生上面现象的原因是 _exit之所以没有显现出来是因为ret的内容进行放到了缓冲区中,exit是可以进行刷新缓冲区的,但是_exit不会进行刷新缓冲区。
- 这个缓冲区到底存在于哪里呢??是操作系统吗?
答案是不是,因为库函数exit就是通过调用系统接口_exit进行实现的,要是这个缓冲区存在于操作系统的话,exit和_exit结果应该是一致的。
其实这个缓冲区存在于用户层面。
进程等待
为什么要进行进程等待?
进程中有一种状态是僵尸状态,进程长期处于僵尸状态会造成内存泄漏,进程等待就是为了解决僵尸进程问题。回收子进程的资源,获取子进程的退出状态。
解决僵尸问题的方法
wait和waitpid
注意事项:头文件两个都必须要进行包含,其中wait和waitpid中的参数status是指向整数的指针,用于存储子进程的退出状态。
观察解决僵尸进程现象
刚开始两个进程处于休眠状态,之所以是休眠状态而不是运行时状态我们之前都已经详细讲过这里就不在进行详细介绍,然后子进程进行退出,此时父进程正在sleep 没有通过wait进行回收子进程,此时的子进程处于僵尸状态,当父进程将子进程的资源进行回收,并且获取子进程退出状态。
wait系列回去子进程的退出信息的本质
wait和waitpid进行解决僵尸进程的问题是通过读取僵尸进程task_struct结构体中的exit_code和exit_signal来进行获取子进程的退出状态的。
wait和waitpid参数的认识
pid
:指定父进程要等待的子进程的进程 ID。这个参数可以有多种取值方式:pid > 0
:父进程等待进程 ID 为pid
的子进程。pid == 0
:父进程等待任何属于同一个进程组的子进程。pid == -1
:父进程等待任何子进程(行为与wait
相同)。pid < -1
:父进程等待进程组 ID 为|pid|
的子进程。
status
:与wait
调用相同,用于接收子进程的退出状态。options
:可以使用以下选项控制waitpid
的行为:WNOHANG
:非阻塞模式,立即返回。如果没有子进程退出,则返回 0。WUNTRACED
:如果子进程停止(例如,通过接收到信号),也返回状态信息。WCONTINUED
:如果子进程在停止后继续运行,返回状态信息
接收进程的退出状态 status 的底层
通过上面的现象我们想要获取的子进程的退出状态是exit返回给父进程的15,但是我们通过status获取的子进程的退出状态竟然是3840这一串数字,其实status获取子进程的退出状态并不是按照整数的整体进行获取的,而是按照以下方式进行编码的。
退出状态的编码
退出状态一般是一个 16 位的整数,具体编码方式如下:
- 低 8 位:包含子进程的退出状态码(对于正常退出是
exit()
返回的值)。 - 高 8 位:用于指示子进程是否因信号终止。
底层的方法
宏封装的方法
阻塞式等待和非阻塞式等待
阻塞式等待
对于options的参数,我们默认是进行传入参数0,此时父进程进行等待子进程退出的方式是阻塞式等待,当子进程并未进行退出时,父进程加入到阻塞队列中,等待CPU的调度,父进程处于休眠状态,当父进程接收到子进程的退出信号时,父进程被激活开始进行回收子进程并获取子进程的退出状态。
操作系统中的阻塞式等待给我们用户层的主管感受就是系统好像卡住了,例如cin 和scanf 在等待我们进行输入时
非阻塞式等待
当将options的参数进行传入1时,此时父进程进行等待子进程的方式时非阻塞式等待,但是我们通常不是通过传入数字1进行的,而是通过定义宏(WNOHANG)进行实现,这样的好处当后续进行阅读代码时的,代码的可读性比较好,非阻塞式等待是当父进程在进行等待子进程退出的过程中还可以继续执行其他进程。非阻塞时需要进行轮回检测。
下面是阻塞等待和非阻塞等待的伪代码