Linux-等待子进程
参考资料:《Linux环境编程:从应用到内核》
僵尸进程
进程退出时会进行内核清理,基本就是释放进程所有的资源,这些资源包括内存资源、文件资源、信号量资源、共享内存资源,或者引用计数减一,或者彻底释放。不过,进程的退出其实并没有将所有的资源完全释放,仍保留了少量的资源,比如进程的PID依然被占用着,不可被系统分配。此时的进程不可运行,事实上也没有地址空间让其运行,进程进入僵尸状态。
僵尸进程依然保留的资源有进程控制块task_struct、内核栈等。这些资源不释放是为了提供一些重要的信息,比如进程为何退出,是收到信号退出还是正常退出,进程退出码是多少,进程一共消耗了多少系统CPU时间,多少用户CPU时间,收到了多少信号,发生了多少次上下文切换,最大内存驻留集是多少,产生多少缺页中断?等等。
父进程通过fork()函数创建子进程后,子进程退出,但父进程没有调用wait()或waitpid()回收子进程的资源的话,这个时候子进程变成僵尸进程。
清除僵尸进程有以下两种方法:
- 父进程调用wait函数或waitpid函数,为子进程“收尸”。
- 父进程退出,init进程会为子进程“收尸”。
一般而言,系统不希望大量进程长期处于僵尸状态,因为会浪费系统资源。除了少量的内存资源外,比较重要的是进程ID。僵尸进程并没有将自己的进程ID归还给系统,而是依然占有这个进程ID,因此系统不能将该ID分配给其他进程。
如果我们不关心子进程的退出状态,就应该将父进程对SIGCHLD的处理函数设置为SIG_IGN,或者在调用sigaction函数时设置SA_NOCLDWAIT标志位。这两者都会明确告诉子进程,父进程很“绝情”,不会为子进程“收尸”。子进程退出的时候,内核会检查父进程的SIGCHLD信号处理结构体是否设置了SA_NOCLDWAIT标志位,或者是否将信号处理函数显式地设为SIG_IGN。如果是,则autoreap为true,子进程发现autoreap为true也就“死心”了,不会进入僵尸状态,而是调用release_task函数“自行了断”了。
等待子进程之wait()
Linux提供了wait()函数来获取子进程的退出状态:
#include <sys/wait.h>pid_t wait(int* status);
成功时,返回已退出子进程的进程ID;失败时,则返回-1并设置errno,常见的errno。

注意父子进程是两个进程,子进程退出和父进程调用wait()函数获取子进程状态在时间上是独立的,所以会出现以下两种情况:
- 子进程先退出,父进程后调用wait()函数
- 父进程先调用wait()函数,子进程后退出
对于第一种情况,子进程执行完毕已经退出,只留下了少量的信息等待父进程回收。当父进程调用wait()函数时候,父进程获取到子进程的状态信息,wait函数立刻返回。
对于第二种情况,如果父进程先调用wait()函数,此时子进程还没退出,wait()函数就会阻塞在这里,直到某个子进程退出。
// 示例#include <iostream>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>int main()
{pid_t pid = fork();if (pid < 0){std::cout << "子进程创建失败" << std::endl;return -1;}else if (pid == 0){// 这是子进程std::cout << "打印子进程的进程ID: " << getpid() << std::endl;sleep(10);}else{// 子进程还没退出,会阻塞在wait函数这里std::cout << "子进程还没退出" << std::endl;// 这是父进程pid_t pc = wait(nullptr);std::cout << "子进程退出, 进程ID: " << pc << std::endl;}return 0;
}[root@Zhn test4]# g++ test1.cpp -o test1
[root@Zhn test4]# ./test1
子进程还没退出
打印子进程的进程ID: 3400
子进程退出, 进程ID: 3400
[root@Zhn test4]#
可以看到,父进程阻塞在wait()函数,等待子进程退出后执行。
wait()函数等待的是任意一个子进程,任何一个子进程退出都会让其立刻返回。当多个子进程都处于僵尸状态时,wait()函数获取到其中一个子进程的信息后立刻返回。由于wait()函数不会接收pid_t类型的参数,所以也不能明确等待某一个子进程的退出。
那么一个进程如何等待所有子进程都返回呢?
wait()函数返回有三种可能性:
- 等到了子进程退出,获取其退出信息,返回值是该子进程的进程ID;
- 等待过程中,收到了信号,信号打断了系统调用,并且注册信号处理函数时并没有设置SA_RESTART标志位,这样的话系统调用就不会重启wait()函数,wait()函数会返回-1,并且errno设置为EINTR;
- 已经成功等待了所有子进程退出,没有子进程的退出信息需要接收,这种情况下,wait()函数返回-1并且errno设置为ECHILD。
《Linux/Unix系统编程手册》给出下面的代码来等待所有子进程的退出:
while((childPid = wait(NULL)) != -1)continue;if(errno !=ECHILD)errExit("wait");
但是这种方法忽略了wait()函数被信号中断这种情况,如果wait()函数被信号中断,上述代码就不能成功等待所有子进程退出。
所以我们需要把上面代码封装以下:
pid_t r_wait(int *stat_loc)
{int retval;// 如果被信号中断,表达式为真,重启wait()函数while(((retval = wait(stat_loc)) == -1 && (errno == EINTR));return retval;
}while((childPid = r_wait(NULL)) != -1)continue;If(errno != ECHILD)
{/*some error happened*/
}
由上面可以看出wait()函数具有一些局限性:
- 不能等待特定的子进程:如果进程存在多个子进程,而它只想获取某一个子进程的退出状态,就需要一一等待,通过返回的进程ID判断是不是自己关心的子进程;
- 如果不存在子进程退出,wait函数就会阻塞:有时候,只是想尝试获取子进程退出的状态,如果没有子进程退出就立刻返回,不需要阻塞等待;
- wait()函数只能发现子进程的终止事件:如果某些子进程因某信号而停止,或者停止的子进程收到SIGCONT信号又恢复执行,这些事wait函数无法获知。
为了解决这三个缺点,引入了waitpid()函数。
等待子进程之waitpid()
waitpid()函数接口如下:
#include <sys/wait.h>pid_t waitpid(pid_t pid, int *status, int options);
waitpid()与wait()相同的地方:
- 返回值的含义相同,都是终止子进程或因信号停止或因信号恢复而执行的子进程的进程ID。
- status的含义相同,都是用来记录子进程的相关事件,后面将会详细介绍。
接下来介绍waitpid()函数特有的功能:
第一个参数是pid_t类型,所以waitpid()可以明确指定要等待哪一个子进程的退出:
- pid > 0:表示等待进程ID为pid的子进程;
- pid = 0:表示等待与调用进程同一个进程组的任意子进程,因为子进程可以设置进程组,那么如果某些子进程和父进程不在同一个进程组,这样的进程就不关心它的退出状态;
- pid = -1:表示等待任意子进程,同wait类似,waitpid(-1, &status, 0)与wait(&status)完全等价;
- pid < -1:等待所有子进程中,进程组ID与pid绝对值相等的子进程。
第二个参数是int*类型:
无论是wait()还是waitpid(),都有一个status变量,这个变量是一个int类型指针。可以传递mullptr,表示不关心子进程退出状态,不为空就可以获得更多子进程的状态。
-
子进程是正常退出的:

-
进程收到信号,导致退出:

-
进程收到信号,被停止:

-
子进程恢复执行

第三个参数options是一个位掩码,可以同时存在多个标志位。如果options没有设置任何标志位,其行为与wait类似,即阻塞等待与pid匹配的进程退出。
options的标志位可以是如下标志位的组合:
- WUNTRACED:除了关心终止子进程的信息,也关心那些因信号而停止的子进程信息。
- WCONTINUED:除了关心终止子进程的信息,也关心那些因收到信号而恢复执行的子进程的状态信息。
- WNOHANG:指定的子进程并未发生状态变化,立刻返回,不会阻塞。这种情况下返回值是0。如果调用进程并没有与pid匹配的子进程,则返回-1,并设置errno为ECHILD,根据返回值和errno可以区分这两种情况。
Linux提供了SIGSTOP(信号值19)和SIGCONT(信号值18)两个信号,来完成暂停和恢复的动作,可以通过执行kill-SIGSTOP或kill-19来暂停一个进程的执行,通过执行kill-SIGCONT或kill-18来让一个暂停的进程恢复执行。
// 示例// 等待子进程之waitpid#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>int main()
{int status;pid_t pid = fork();if (pid < 0){std::cout << "子进程创建失败" << std::endl;return -1;}else if (pid == 0){std::cout << "这是子进程: " << getpid() << std::endl;sleep(3);exit(3);}else{std::cout << "这是父进程: " << getpid() << std::endl;pid_t pc = waitpid(0, &status, WNOHANG);if (pc == 0)std::cout << "此时没有子进程退出" << std::endl;else if (WIFEXITED(status))std::cout << "子进程: " << pc << "正常退出, 退出状态为" << WEXITSTATUS(status) << std::endl;elsestd::cout << "子进程: " << pc << "非正常退出" << std::endl;}return 0;
}[root@Zhn test4]# g++ test2.cpp -o test2
[root@Zhn test4]# ./test2
这是父进程: 4551
此时没有子进程退出
这是子进程: 4552
[root@Zhn test4]#
示例中,父进程waitpid函数的参数设置的意思是等待父进程同一进程组的任意子进程退出事件,如果没有子进程退出,则返回值为0,子进程中睡眠了3秒,这时父进程调用waitpid,发现没有子进程退出,所以返回值为0。
这是一个简单的小例子,其他例子都是举一反三。
相关文章:
Linux-等待子进程
参考资料:《Linux环境编程:从应用到内核》 僵尸进程 进程退出时会进行内核清理,基本就是释放进程所有的资源,这些资源包括内存资源、文件资源、信号量资源、共享内存资源,或者引用计数减一,或者彻底释放。…...
【LeetCode热题100】【二叉树】二叉树的最大深度
题目链接:104. 二叉树的最大深度 - 力扣(LeetCode) 最大深度等于左子树的最大深度和右子树的最大深度中的较大者加一 class Solution { public:int maxDepth(TreeNode *root) {if (!root)return 0;return max(maxDepth(root->left), max…...
想做产品经理,应该选择什么专业?
产品经理作为互联网公司的核心职位,一直以来备受关注。随着互联网的不断发展,产品经理的需求也越来越高,很多人都想要了解哪些专业适合做产品经理。本文将为大家介绍几个适合做产品经理的专业。 1、心理学相关专业 C端产品工作的本源&#x…...
[机器学习Day 1~3
[机器学习]Day 1~3 数据预处理第1步:导入库第2步:导入数据集第3步:处理丢失数据第4步:解析分类数据创建虚拟变量 第5步:拆分数据集为训练集合和测试集合第6步:特征量化 简单线性回归模型第一步:…...
Day106:代码审计-PHP原生开发篇文件安全上传监控功能定位关键搜索1day挖掘
目录 emlog-文件上传&文件删除 emlog-模板文件上传 emlog-插件文件上传 emlog-任意文件删除 通达OA-文件上传&文件包含 知识点: PHP审计-原生开发-文件上传&文件删除-Emlog PHP审计-原生开发-文件上传&文件包含-通达OA emlog-文件上传&文件…...
数码视讯Q7盒子刷armbian遇到的坑之二
继续,nand的q7 搜遍全网,这个盒子能用的安卓映像有两个,一个本站付费下载的那个,另一个是20191218-Q7-nand-4.4.2-root-twrp-Milton这个映像(具体地址自己搜索吧)。第一个需要license,需要自己…...
vue2 使用vue-org-tree demo
1.安装 npm i vue2-org-tree npm install -D less-loader less安装 less-loader出错解决办法,直接在package.json》devDependencies下面加入less和less-loader版本,然后执行npm i ,我用的nodejs版本是 16.18.0,“webpack”: “^4…...
【数据结构】考研真题攻克与重点知识点剖析 - 第 7 篇:查找
前言 本文基础知识部分来自于b站:分享笔记的好人儿的思维导图与王道考研课程,感谢大佬的开源精神,习题来自老师划的重点以及考研真题。此前我尝试了完全使用Python或是结合大语言模型对考研真题进行数据清洗与可视化分析,本人技术…...
【数仓】DataX 通过SpringBoot项目自动生成 job.json 文件
相关文章 【数仓】基本概念、知识普及、核心技术【数仓】数据分层概念以及相关逻辑【数仓】Hadoop软件安装及使用(集群配置)【数仓】Hadoop集群配置常用参数说明【数仓】zookeeper软件安装及集群配置【数仓】kafka软件安装及集群配置【数仓】flume软件安…...
注解式 WebSocket - 构建 群聊、单聊 系统
目录 前言 注解式 WebSocket 构建聊天系统 群聊系统(基本框架) 群聊系统(添加昵称) 单聊系统 WebSocket 作用域下无法注入 Spring Bean 对象? 考虑离线消息 前言 很久之前,咱们聊过 WebSocket 编程式…...
无线游戏手柄的测试(Windows11系统手柄调试方法)
实物 1、把游戏手柄的无线接收器插入到电脑usb接口中 2、【控制面板】----【查看设备和打印机】 3、【蓝牙和其它设备】--【更多设备和打印机设置】 4、鼠标右键【游戏控制器设置】 5、【属性】 6、【测试】(每个按键是否正常) 7、【校准】(…...
计算机的各种转换
一、存量容量的转换 特别注意:1 B 8 bit 转换为:1024 2(10) 括号中的数字为2的指数(即多少次方) 1KB2(10)B1024B; 括号中的数字为2的指数(即多少次方) 1MB2(10)KB1024KB2(20)B; 1GB2(10)MB1024MB2(3…...
Git分布式版本控制系统——Git常用命令(一)
一、获取Git仓库--在本地初始化仓库 执行步骤如下: 1.在任意目录下创建一个空目录(例如GitRepos)作为我们的本地仓库 2.进入这个目录中,点击右键打开Git bash窗口 3.执行命令git init 如果在当前目录中看到.git文件夹&#x…...
【Node.js】短链接
原文链接:Nodejs 第六十二章(短链接) - 掘金 (juejin.cn) 短链接是一种缩短长网址的方法,将原始的长网址转换为更短的形式。短链接的主要用途之一是在社交媒体平台进行链接分享。由于这些平台对字符数量有限制,长网址可…...
详解 Redis 在 Centos 系统上的安装
文章目录 详解 Redis 在 Centos 系统上的安装1. 使用 yum 安装 Redis 52. 创建符号链接3. 修改配置文件4. 启动和停止 Redis 详解 Redis 在 Centos 系统上的安装 1. 使用 yum 安装 Redis 5 如果是Centos8,yum 仓库中默认的 redis 版本就是5,直接 yum i…...
C语言 | Leetcode C语言题解之第17题电话号码的字母组合
题目: 题解: char phoneMap[11][5] {"\0", "\0", "abc\0", "def\0", "ghi\0", "jkl\0", "mno\0", "pqrs\0", "tuv\0", "wxyz\0"};char* digits…...
wordpress全站开发指南-面向开发者及深度用户(全中文实操)--wordpress中的著名循环
wordpress中的著名循环 首先,在深入研究任何代码之前,我们首先要确保我们有不止一篇博客文章可以工作。因此,我们要去自己的wordpress站点,从侧边栏单机Posts(文章),进行创建 在执行代码的时候会优先执行single.php如…...
libVLC 提取视频帧使用QGraphicsView渲染
在前面章节中,我们讲解了如何使用QWidget渲染每一帧视频数据,这种方法对 CPU 负荷较高。 libVLC 提取视频帧使用QWidget渲染-CSDN博客 后面又讲解了使用OpenGL渲染每一帧视频数据,使用 OpenGL去绘制,利用 GPU 减轻 CPU 计算负荷…...
大厂Java笔试题之判断字母大小写
/*** 题目:如果一个由字母组成的字符串,首字母是大写,那么就统计该字符串中大写字母的数量,并输出该字符串中所有的大写字母。否则,就输出* 该字符串不是首字母大写*/ public class Demo2 {public static void main(St…...
场景文本检测识别学习 day02(AlexNet论文阅读、ResNet论文精读)
怎么读论文 在第一遍阅读的时候,只需要看题目,摘要和结论,先看题目是不是跟我的方向有关,看摘要是不是用到了我感兴趣的方法,看结论他是怎么解决摘要中提出的问题,或者怎么实现摘要中的方法,然…...
超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
