进程控制之进程等待

1 进程等待必要性

• 之前讲过,子进程退出,父进程如果不管不顾,就可能造成'僵尸进程'的问题,进而造成内存泄漏。

• 另外,进程一旦变成僵尸状态,那就刀枪不入,"杀人不眨眼"的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。

• 最后,父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出。

• 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息

在进程等待中,回收子进程资源是必须的,获取子进程退出信息是可选的,因为有些进程不需要退出消息

进程等待是什么,给它下一个定义

进程等待是让父进程通过等待的方式回收子进程PCB,解决Z状态,如果需要,获取子进程退出信息

2 进程等待的方法

• 如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。

• 如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。

• 如果不存在该子进程,则立即出错返回。

wait

cpp 复制代码
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int* status);
返回值:
成功返回被等待进程pid,失败返回-1。
参数:
输出型参数,获取子进程退出状态,不关心则可以设置成为NULL

父进程调用wait表示父进程在等待任意一个子进程:

1 如果子进程没有退出,父进程wait的时候就会阻塞

2 如果子进程退出,父进程wait的时候,wait就会返回,让系统自动解决子进程的僵尸问题

wait调用实际并不重要,我们只用它来验证进程等待解决僵尸问题,重要的是下一个waitpid

通过下面这段程序我们可以看到父进程调用wait解决子进程的僵尸状态

下面我们来看一下wait的阻塞状态

如果我想要回收多进程呢?

waitpid

wait功能是waitpid功能的子集,进程等待的最佳实践是waitpid

cpp 复制代码
pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:
pid:
Pid=-1,等待任一个子进程。与wait等效。
Pid>0.等待其进程ID与pid相等的子进程。
status: 输出型参数
宏:更加方便的提取退出信息
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
    
options:
默认为0,表示阻塞等待
WNOHANG: 非阻塞等待,若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。
         若正常结束,则返回该子进程的ID。

输出型参数

核心定义

输出型参数 是一种特殊的函数/方法参数,其主要目的不是向函数内部传递数据,而是为函数提供一个"容器"或"地址",用于将函数内部的计算结果带回到调用处

简单来说,它是一种让函数能够返回多个值的有效方法(因为通常函数只能直接返回一个值)。

主要特点:

1 单向传递(出) :与输入参数(数据从外部传入函数内部)相反,输出型参数的数据流动方向是从函数内部到外部

2 不关心初始值 :在调用函数前,传递给输出型参数的变量不需要初始化。即使初始化了,函数内部也会忽略其初始值,并覆盖它。

3 必须在函数内赋值 :函数内部必须对输出型参数进行赋值。在编译型语言(如C#)中,如果函数没有给所有输出参数赋值,编译器会报错。

status

核心定义

当父进程使用 wait,waitpid 或相关的系统调用等待其子进程终止或暂停时,内核会通过 status参数向父进程传递一个整型数字 。这个数字本身是一个位图(bitmask),它不是一个简单的退出码,而是一个包含了多种信息的编码值。

简单来说,ststus是一个打包好的数据包裹,父进程需要调用特定的宏(macro)来解开这个包裹,才能获取子进程退出的具体原因和详情。

• wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。

• 如果传递NULL,表示不关心子进程的退出状态信息。

• 否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。

• status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图:

status共有32个比特位,高16不考虑,我们只研究status低16比特位

代码验证:

(status>>8)&0xFF表示退出码,status&0x7F表示信号

下面验证一下异常退出:

异常退出时退出码无意义

也可以像下面这样更方便的查看退出信息

非阻塞等待

非阻塞等待时waitpid返回值有三种情况

一般情况下在非阻塞等待时我们采用非阻塞轮询方案,即不停的询问是否等待成功直至等待成功或失败

相对于阻塞等待,非阻塞等待更加高效,因为它不会卡住父进程,父进程在等待的间隙可以做其他事情

到此,进程控制之进程等待就讲完了,怎么样,是不是感觉大脑里面多了很多新知识。

如果觉得博主讲的还可以的话,就请大家多多支持博主,收藏加关注,追更不迷路

如果觉得博主哪里讲的不到位或是有疏漏,还请大家多多指出,博主一定会加以改正

博语小屋将持续为您推出文章

相关推荐
Dobby_053 小时前
【Linux】网络安全管理:SELinux 和 防火墙联合使用 | Redhat
linux·运维·云原生·防火墙·selinux
wheeldown4 小时前
【Linux】Linux进程状态和僵尸进程:一篇看懂“进程在忙啥”
linux·运维·服务器·进程
jzwalliser4 小时前
关于Linux生态的补充
linux·语言暴力
半桔4 小时前
【Linux手册】动静态库:从原理到制作
linux·运维·服务器·动态库
z202305084 小时前
Linux之块设备的多队列的实现机制
linux·运维·服务器
liulilittle4 小时前
Unix/Linux 平台通过 IP 地址获取接口名的 C++ 实现
linux·开发语言·c++·tcp/ip·unix·编程语言
阿贤Linux4 小时前
设置网卡名称为传统命名方式
linux·ubuntu
码界奇点4 小时前
从零构建Linux Shell解释器深入理解Bash进程创建机制
linux·运维·解释器模式·bash·ux·源代码管理
闻道且行之4 小时前
嵌入式|Linux中打开视频流的两种方式V4l2和opencv
linux·笔记·opencv·嵌入式