进程控制之进程等待

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返回值有三种情况

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

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

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

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

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

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

相关推荐
Lsir10110_13 分钟前
【Linux】进程信号(上半)
linux·运维·服务器
开开心心就好1 小时前
发票合并打印工具,多页布局设置实时预览
linux·运维·服务器·windows·pdf·harmonyos·1024程序员节
予枫的编程笔记1 小时前
【Linux进阶篇】从基础到实战:grep高亮、sed流编辑、awk分析,全场景覆盖
linux·sed·grep·awk·shell编程·文本处理三剑客·管道命令
Sheep Shaun1 小时前
揭开Linux的隐藏约定:你的第一个文件描述符为什么是3?
linux·服务器·ubuntu·文件系统·缓冲区
Tfly__1 小时前
在PX4 gazebo仿真中加入Mid360(最新)
linux·人工智能·自动驾驶·ros·无人机·px4·mid360
陈桴浮海1 小时前
【Linux&Ansible】学习笔记合集二
linux·学习·ansible
生活很暖很治愈2 小时前
Linux——环境变量PATH
linux·ubuntu
?re?ta?rd?ed?2 小时前
linux中的调度策略
linux·运维·服务器
深圳市九鼎创展科技2 小时前
瑞芯微 RK3399 开发板 X3399 评测:高性能 ARM 平台的多面手
linux·arm开发·人工智能·单片机·嵌入式硬件·边缘计算
hweiyu002 小时前
Linux 命令:tr
linux·运维·服务器