Linux进程调度与等待:背后的机制与实现
个人主页:chian-ocean
文章专栏-Linux
前言:
当一个进程发起某种操作(如I/O请求、信号、锁的获取等),但该操作需要的资源暂时不可用时,进程会被操作系统挂起,进入“等待队列”或“阻塞状态”。在此期间,进程不占用CPU,但仍保留其内存、文件描述符等资源

进程等待的必要性
僵尸进程的存在
僵尸进程的成因
- 当子进程终止后,它的退出状态需要由父进程通过调用
wait()或waitpid()系统调用回收。 - 如果父进程未回收子进程的退出状态,子进程会以“僵尸进程”的形式保留在进程表中。
特征:
- 在 Linux 系统中,可以用
ps命令查看,僵尸进程的状态为Z(Zombie)。 - 僵尸进程是操作系统保留的一个条目,主要用于父进程检查子进程的退出状态。
如下:

从图片中可以看到一个典型的 僵尸进程 的现象:
- 进程
27864被强制终止(kill -9 27864),但它的父进程(27863)没有调用wait()或waitpid()来回收其子进程的退出状态。 - 因此,
27864被标记为<defunct>状态,即僵尸进程。 ps输出的STAT列中显示Z+,这是僵尸进程的状态标识。
进程等待
进程等待是操作系统中一种重要的状态,指的是某个进程由于资源不足或条件未满足,暂时无法继续执行而被挂起的现象。
- 使用
wait()或waitpid()回收子进程
wait ( )
参数:
-
int *status:- 用于保存子进程的状态信息(如退出码或终止信号)。
- 如果不需要获取子进程状态,可以将其传入
NULL。
返回值:
- 成功:
- 返回已终止的子进程的 PID。
- 失败:
- 返回
-1,并设置errno。 - 常见错误包括:
ECHILD:当前进程没有子进程。EINTR:调用被信号中断。
- 返回
wait() 的作用
- 阻塞父进程:
wait()会阻塞父进程,直到任意一个子进程状态发生变化(通常是终止)。
- 回收子进程资源:
- 子进程终止后,其资源仍然保留在系统中,直到父进程调用
wait()或waitpid()回收它。 - 如果父进程不调用
wait()或waitpid(),子进程会变成 僵尸进程。
- 子进程终止后,其资源仍然保留在系统中,直到父进程调用
示例:
#include<iostream>
#include<unistd.h>
#include <sys/types.h>
#include <sys/wait.h>using namespace std;void childtast()
{for(int i = 0; i < 10; i++) // 循环打印从 0 到 9 的数字{cout << i << endl; // 输出当前的循环变量 i}sleep(3); // 睡眠 3 秒,模拟子进程的运行延迟
}int main()
{pid_t id = fork(); // 创建子进程cout << "id" << ":" << id << endl;if(id == 0) // 判断是否是子进程{sleep(3); // 子进程先睡眠 3 秒childtast(); // 子进程调用 childtast(),打印数字并睡眠}// 父进程等待任意一个子进程终止pid_t ret = wait(NULL); // 父进程调用 wait(),阻塞等待子进程终止if(ret == id) // 判断 wait() 返回的进程 ID 是否是创建的子进程 ID{cout << "ret" << ":" << ret << endl; // 输出子进程的 IDcout << "wait success" << endl; // 输出等待成功的消息}sleep(3); // 父进程再睡眠 3 秒,模拟延迟return 0;
}
fork() 创建子进程:
- 父进程和子进程同时运行。
- 父进程的
id是子进程的 PID,子进程的id是 0。
子进程的任务:
- 子进程先睡眠 3 秒,然后执行
childtast(),打印0到9。
父进程的等待:
- 父进程调用
wait(NULL),阻塞自身,直到子进程终止。 - 当子进程完成任务并退出后,
wait()返回子进程的 PID。
父进程的后续操作:
- 父进程输出子进程的
PID和等待成功的消息。 - 父进程再睡眠 3 秒后退出。
waitpid ( )
waitpid() 是 wait() 的增强版本,提供了更灵活的功能,允许父进程:
- 等待特定的子进程。
- 非阻塞等待子进程。
- 获取子进程的状态(如退出状态或被信号终止)。
pid_t waitpid(pid_t pid, int *status, int options);
参数说明
-
pid:-
pid > 0:等待特定的子进程(指定的 PID)。 -
pid == 0:等待与当前进程同一个进程组的任意子进程。 -
pid < -1:等待进程组 ID 为|pid|的任意子进程。wait(NULL) //等价于 waitpid(-1,NULL,0); -
pid == -1:等效于wait(),等待任意子进程。
-
status 字段的结构
status:
- 指向一个整数的指针,用于存储子进程的状态信息(退出状态、信号等)。
- 若不关心状态信息,可将其设为
NULL。
在 Linux 系统中,status 是一个整数,表示子进程状态的多种可能性,底层通过位字段表示:
| 位字段 | 含义 |
|---|---|
| 位 0-7 | 子进程退出的信号或退出码(低 8 位)。 |
| 位 8-15 | 退出状态(高 8 位,存储正常退出码)。 |
| 位 16-23 | 暂停信号编号。 |
代码解析字段
#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;
int main()
{ pid_t id = fork(); cout << "id" << ":" << id <<endl; if(id == 0) { sleep(3); exit(1); } int status; pid_t ret = waitpid(-1,&status,0); if(ret == id) {cout << "ret" << ":" << ret <<endl; cout<< "wait success" <<endl; } cout <<"status :" << status << endl;cout << "退出码" << ((status >> 8)& 0xff ) <<" "<< "信号码" << (status & 0x7f)<< endl;return 0;
}
完整运行流程
fork() 创建子进程:
- 父进程创建子进程,并返回子进程的 PID。
子进程逻辑:
- 子进程休眠 3 秒后正常退出,退出码为
1。
父进程逻辑:
- 父进程调用
waitpid()阻塞等待子进程终止。 - 获取子进程的状态信息,并解析退出码和信号码。
父进程输出状态信息:
- 输出子进程的 PID、状态值、退出码和信号码。
解析逻辑:
-
退出码:
(status >> 8) & 0xff- 获取高 8 位的退出码。
-
信号码:
status & 0x7f- 获取低 7 位的信号码.
示例1:进程正常退出的退出码。

示例2:提取被9号信号杀死的进程信号码

id:1667 // 父进程输出,子进程的 PID 是 1667 id:0 // 子进程输出,表明当前是子进程 ret:1667 // 父进程成功等待到子进程结束,返回子进程 PID wait success // 父进程确认子进程终止 status :9 // 父进程获取子进程状态值为 9 退出码0 信号码9 // 父进程解析状态值:// - 退出码 0:子进程未通过 exit() 返回退出码// - 信号码 9:子进程被 SIGKILL 信号终止库中提供的宏替换
解析退出码和信号编号
WIFEXITED(status):- 如果为真,表示子进程正常退出,其退出码存储在高 8 位。
- 使用
(status >> 8) & 0xff提取退出码。
WEXITSTATUS(status):- 获取退出码的宏,等价于
(status >> 8) & 0xff。 - 必须确保
WIFEXITED(status)为真后使用。
- 获取退出码的宏,等价于
解析退出码和信号编号
WIFEXITED(status)- 如果为真,表示子进程正常退出,其退出码存储在高 8 位。
- 使用
(status >> 8) & 0xff提取退出码。
WEXITSTATUS(status):==status & 0x7f- 获取退出码的宏,
- 必须确保
WIFEXITED(status)为真后使用。
options参数介绍
阻塞与非阻塞
| 特性 | 阻塞 | 非阻塞 |
|---|---|---|
| 进程状态 | 等待资源时挂起,无法执行其他任务。 | 立即返回,不会挂起,进程可执行其他任务。 |
| 适用场景 | 简单任务、对实时性要求不高的任务。 | 多任务并发、实时性要求高的任务。 |
| 复杂性 | 实现简单,逻辑清晰。 | 逻辑复杂,需要轮询或回调处理资源状态。 |
| CPU 使用 | 不浪费 CPU 资源,进程处于挂起状态。 | 需要轮询资源状态,可能增加 CPU 占用。 |
| 资源管理 | 等待资源的管理交由操作系统处理。 | 需要程序主动检查资源状态,增加开发复杂度。 |
options:
- 用于指定额外的选项:
0:阻塞等待。WNOHANG:非阻塞等待。WUNTRACED:返回暂停的子进程状态(子进程因SIGSTOP信号暂停)。WCONTINUED:返回恢复运行的子进程状态(子进程因SIGCONT信号继续运行)。
WNOHANG
- 非阻塞模式:
- 如果没有子进程终止,
waitpid()会立即返回,而不是阻塞父进程。
- 如果没有子进程终止,
- 返回值:
- 如果有子进程状态变化,则返回子进程的 PID。
- 如果没有子进程状态变化,则返回
0。
非阻塞轮询
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <chrono>
#include <thread>
using namespace std;
int main() {pid_t pid = fork(); // 创建子进程if (pid == 0) {// 子进程逻辑cout << "Child process running..." << endl;sleep(5); // 模拟子进程任务,延迟 5 秒cout << "Child process exiting..." << endl;exit(42); // 子进程以退出码 42 正常退出} else if (pid > 0) {// 父进程逻辑int status;while (true) {pid_t ret = waitpid(-1, &status, WNOHANG); // 非阻塞检查子进程状态if (ret == 0) {// 子进程尚未终止,父进程继续其他工作cout << "Child process still running. Parent doing other work..." << endl;this_thread::sleep_for(chrono::seconds(1)); // 模拟父进程任务} else if (ret > 0) {// 子进程已终止,解析状态if (WIFEXITED(status)) {cout << "Child process " << ret << " exited with code " << WEXITSTATUS(status) << endl;} else if (WIFSIGNALED(status)) {cout << "Child process " << ret << " was terminated by signal " << WTERMSIG(status) << endl;}break; // 结束轮询} else {// waitpid 出错perror("waitpid failed");break;}}} else {// fork 失败perror("fork failed");return 1;}return 0;
}
执行结果:

多进程下的进程等待
阻塞等待多个子进程
示例代码:等待所有子进程完成
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;int main() {// 创建多个子进程for (int i = 0; i < 3; ++i) {pid_t pid = fork();if (pid == 0) {// 子进程cout << "Child " << i << " (PID: " << getpid() << ") running..." << endl;sleep(2 + i); // 每个子进程休眠不同时间cout << "Child " << i << " (PID: " << getpid() << ") exiting..." << endl;exit(i); // 子进程以其序号为退出码}}// 父进程:等待所有子进程完成int status;while (true) {pid_t ret = wait(&status); // 阻塞等待任意一个子进程结束if (ret == -1) {// 没有子进程可等待时退出循环cout << "All child processes have finished." << endl;break;// 解析子进程状态if (WIFEXITED(status)) {cout << "Child process " << ret << " exited with code: " << WEXITSTATUS(status) << endl;} else if (WIFSIGNALED(status)) {cout << "Child process " << ret << " was terminated by signal: " << WTERMSIG(status) << endl;}}return 0;
}
代码执行:

非阻塞轮询等待多个子进程
示例代码:非阻塞等待多个子进程
通过 waitpid() 配合 WNOHANG 实现父进程的非阻塞轮询,定期检查是否有子进程完成。
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <chrono>
#include <thread>
using namespace std;int main() {// 创建多个子进程for (int i = 0; i < 3; ++i) {pid_t pid = fork();if (pid == 0) {// 子进程cout << "Child " << i << " (PID: " << getpid() << ") running..." << endl;sleep(2 + i); // 每个子进程休眠不同时间cout << "Child " << i << " (PID: " << getpid() << ") exiting..." << endl;exit(i); // 子进程以其序号为退出码}}// 父进程:非阻塞轮询等待所有子进程完成int status;int completed = 0; // 已完成的子进程计数while (completed < 3) {pid_t ret = waitpid(-1, &status, WNOHANG); // 非阻塞检查子进程状态if (ret > 0) {// 有子进程状态变化completed++;if (WIFEXITED(status)) {cout << "Child process " << ret << " exited with code: " << WEXITSTATUS(status) << endl;} else if (WIFSIGNALED(status)) {cout << "Child process " << ret << " was terminated by signal: " << WTERMSIG(status) << endl;}} else if (ret == 0) {// 没有子进程状态变化,父进程继续其他工作cout << "No child process exited yet. Parent doing other work..." << endl;this_thread::sleep_for(chrono::seconds(1)); // 模拟其他任务} else {// 错误处理perror("waitpid failed");break;}}cout << "All child processes have finished." << endl;return 0;
}
代码执行:

相关文章:
Linux进程调度与等待:背后的机制与实现
个人主页:chian-ocean 文章专栏-Linux 前言: 当一个进程发起某种操作(如I/O请求、信号、锁的获取等),但该操作需要的资源暂时不可用时,进程会被操作系统挂起,进入“等待队列”或“阻塞状态”。…...
网易云音乐歌名可视化:词云生成与GitHub-Pages部署实践
引言 本文将基于前一篇爬取的网易云音乐数据, 利用Python的wordcloud、matplotlib等库, 对歌名数据进行深入的词云可视化分析. 我们将探索不同random_state对词云布局的影响, 并详细介绍如何将生成的词云图部署到GitHub Pages, 实现数据可视化的在线展示. 介绍了如何从原始数据…...
单片机基础模块学习——DS18B20温度传感器芯片
不知道该往哪走的时候,就往前走。 一、DS18B20芯片原理图 该芯片共有三个引脚,分别为 GND——接地引脚DQ——数据通信引脚VDD——正电源 数据通信用到的是1-Wier协议 优点:占用端口少,电路设计方便 同时该协议要求通过上拉电阻…...
《网络数据安全管理条例》施行,企业如何推进未成年人个人信息保护(下)
文章目录 前言三、全流程推进未成年人个人信息保护1、处理前:未成年人个人信息处理的告知同意2、处理中:加强个人信息处理流程管理3、处理后:落实个人信息保护合规审计四、大型网络平台应每年发布社会责任报告前言 《网数条例》颁布前,我国已针对未成年人个人信息保护陆续…...
书生大模型实战营3
文章目录 L0——入门岛git基础Git 是什么?Git 中的一些基本概念工作区、暂存区和 Git 仓库区文件状态分支主要功能 Git 平台介绍GitHubGitLabGitee Git 下载配置验证下载 Git配置 Git验证 Git配置 Git常用操作Git简易入门四部曲Git其他指令 闯关任务任务1: 破冰活动…...
Spring Boot 集成 WebClient 实战教程 实现同步、异步请求处理以及响应式编程、响应式流、响应式Mono
该项目介绍springboot集成WebClient 实现服务的请求操作 示例中演示了,如何配置WebClient的请求头,请求参数等相关参数,实现同步、异步请求处理以及响应式编程、响应式流、响应式Mono。 为什么使用WebClient 不用RestTemplate 在 Spring Framework 5.0 及更高版本中,Res…...
C语言深入解析 printf的底层源码实现
深入解析 printf 的底层源码实现 printf 是 C 标准库中最常用的函数之一,用于格式化输出字符串。它的底层实现复杂且高效,包含多个模块化的函数和机制。本文结合 GNU C Library(glibc)的源码,详细分析 printf 的实现原…...
go 循环处理无限极数据
数据表结构: CREATE TABLE permission (id int(11) NOT NULL AUTO_INCREMENT COMMENT 权限ID,permission_name varchar(255) DEFAULT NULL COMMENT 权限名称,permission_url varchar(255) DEFAULT NULL COMMENT 权限路由,status tinyint(1) DEFAULT NULL COMMENT 权…...
C# Dynamic关键字
一、引言:开启动态编程之门 在 C# 的编程世界里,长久以来我们习惯了静态类型语言带来的严谨与稳定。在传统的 C# 编程中,变量的类型在编译时就已经确定,这就像是给每个变量贴上了一个固定的标签,在整个代码执行过程中…...
ReactNative react-devtools 夜神模拟器连调
目录 一、安装react-devtools 二、在package.json中配置启动项 三、联动 一、安装react-devtools yarn add react-devtools5.3.1 -D 这里选择5.3.1版本,因为高版本可能与夜神模拟器无法联动,导致部分功能无法正常使用。 二、在package.json中配置启…...
【教学类-89-02】20250128新年篇02——姓名藏头对联(星火讯飞+Python,五言对联,有横批)
背景需求: 过年了,我想用幼儿的名字写对联,但是我根本不会写,于是尝试让AI来写。 1.我班的孩子的名字都是2字和3字的 2.惊喜发现,AI它很快就能生成带名字的对联 但是观察发现,如果是二个名字的对联&#…...
装机爱好者的纯净工具箱
对于每一位电脑用户来说,新电脑到手后的第一件事通常是检测硬件性能。今天为大家介绍一款开源且无广告的硬件检测工具——入梦工具箱。 主要功能 硬件信息一目了然 打开入梦工具箱,首先看到的是硬件信息概览。这里不仅包含了内存、主板、显卡、硬盘等常…...
【新春不断更】数据结构与算法之美:二叉树
Hello大家好,我是但凡!很高兴我们又见面啦! 眨眼间已经到了2024年的最后一天,在这里我要首先感谢过去一年陪我奋斗的每一位伙伴,是你们给予我不断前行的动力。银蛇携福至,万象启新程。蛇年新春之际…...
网站结构优化:加速搜索引擎收录的关键
本文来自:百万收录网 原文链接:https://www.baiwanshoulu.com/9.html 网站结构优化对于加速搜索引擎收录至关重要。以下是一些关键策略,旨在通过优化网站结构来提高搜索引擎的抓取效率和收录速度: 一、合理规划网站架构 采用扁…...
Effective Objective-C 2.0 读书笔记—— objc_msgSend
Effective Objective-C 2.0 读书笔记—— objc_msgSend 文章目录 Effective Objective-C 2.0 读书笔记—— objc_msgSend引入——静态绑定和动态绑定OC之中动态绑定的实现方法签名方法列表 其他方法objc_msgSend_stretobjc_msgSend_fpretobjc_msgSendSuper 尾调用优化总结参考文…...
[MySQL]事务的隔离级别原理与底层实现
目录 1.为什么要有隔离性 2.事务的隔离级别 读未提交 读提交 可重复读 串行化 3.演示事务隔离级别的操作 查看与设置事务的隔离级别 演示读提交操作 演示可重复读操作 1.为什么要有隔离性 在真正的业务场景下,MySQL服务在同一时间一定会有大量的客户端进程…...
项目升级Sass版本或升级Element Plus版本遇到的问题
项目升级Sass版本或升级Element Plus版本遇到的问题 如果项目有需求需要用到高版本的Element Plus组件,则需要升级相对应的sass版本,Element 文档中有提示,2.8.5及以后得版本,sass最低支持的版本为1.79.0,所升级sass、…...
C++中,存储两个相同类型的数据,数据结构
在C中,存储两个相同类型的数据,可以使用多种数据结构。这里有几种常见且合适的选择: 简单的变量: 最直接的方式就是使用两个独立的变量。这种方法简单直观,但不够结构化。 cpp int a 5; int b 10; std::pair&#x…...
python实战(十五)——中文手写体数字图像CNN分类
一、任务背景 本次python实战,我们使用来自Kaggle的数据集《Chinese MNIST》进行CNN分类建模,不同于经典的MNIST数据集,我们这次使用的数据集是汉字手写体数字。除了常规的汉字“零”到“九”之外还多了“十”、“百”、“千”、“万”、“亿…...
[论文阅读] (37)CCS21 DeepAID:基于深度学习的异常检测(解释)
祝大家新春快乐,蛇年吉祥! 《娜璋带你读论文》系列主要是督促自己阅读优秀论文及听取学术讲座,并分享给大家,希望您喜欢。由于作者的英文水平和学术能力不高,需要不断提升,所以还请大家批评指正࿰…...
23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist
现象: android studio报错: [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决: 不要动CMakeLists.…...
MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...
零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程
STM32F1 本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增…...
渗透实战PortSwigger靶场:lab13存储型DOM XSS详解
进来是需要留言的,先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码,输入的<>当成字符串处理回显到页面中,看来只是把用户输…...
热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁
赛门铁克威胁猎手团队最新报告披露,数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据,严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能,但SEMR…...
初探用uniapp写微信小程序遇到的问题及解决(vue3+ts)
零、关于开发思路 (一)拿到工作任务,先理清楚需求 1.逻辑部分 不放过原型里说的每一句话,有疑惑的部分该问产品/测试/之前的开发就问 2.页面部分(含国际化) 整体看过需要开发页面的原型后,分类一下哪些组件/样式可以复用,直接提取出来使用 (时间充分的前提下,不…...
