进程控制之进程等待

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

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

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

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

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

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

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

相关推荐
HZero.chen1 天前
Linux字符串处理
linux·string
张童瑶1 天前
Linux SSH隧道代理转发及多层转发
linux·运维·ssh
汪汪队立大功1231 天前
什么是SELinux
linux
石小千1 天前
Linux安装OpenProject
linux·运维
柏木乃一1 天前
进程(2)进程概念与基本操作
linux·服务器·开发语言·性能优化·shell·进程
Lime-30901 天前
制作Ubuntu 24.04-GPU服务器测试系统盘
linux·运维·ubuntu
百年渔翁_肯肯1 天前
Linux 与 Unix 的核心区别(清晰对比版)
linux·运维·unix
胡闹541 天前
Linux查询防火墙放过的端口并额外增加需要通过的端口命令
linux·运维·windows
lc9991021 天前
简洁高效的相机预览
android·linux
SongJX_1 天前
DHCP服务
linux·运维·服务器