Linux 进程控制:进程终止与等待・waitpid 选项参数与状态解析(告别僵尸进程)

Linux 进程控制:进程终止与等待・waitpid 选项参数与状态解析(告别僵尸进程)
Sunday不上发条在这里祝大家新的一年,bug 退散,需求减半,代码一次跑通,薪资节节攀升!🎉🎉🎉🎉

一、进程终止

即正在执行的程序停止执行,操作系统进行系统资源释放(进程申请的相关内核数据结构和代码数据)。

进程是用来完成某个任务的,所以结束时无非三种情况:

代码运行完毕,结果正确

代码运行完毕,结果不正确

代码异常终止

1、退出码

我们在以前写main函数时,总在最后返回一个0,这个0其实就是退出码。0就表示我们的程序运行完毕,结果正确;结果不正确就可能返回其他非0 的退出码。那怎么查看这些退出码呢?

退出码:进程终止时返回给操作系统一个整数(0~133),用来标识进程的终止状态。

我们可以借助strerror函数,strerror 是 C 标准库中的核心函数(定义在<string.h>头文件),核心作用是将系统的「错误码(errno)」转换为人类可读的字符串描述。

2、常见退出方法

2.1 正常终止
💦 main函数返回

我们可以通过 echo &? 查看进程退出码。

💦 _exit



参数:status 定义了进程的终止状态,父进程通过wait来获取该值。

• 说明:虽然status是int,但是仅有低8位可以被父进程所用。所以_exit(-1)时,在终端执行$?发现 返回值是255。



💦 exit





📢 那么_exit 和 exit 有什么区别呢?

_exit 属于系统调用(内核态底层),而exit 属于标准库中的函数。



exit 在底层调用了 _exit, 但在此之前还进行了执行用户定义的清理函数和刷新缓冲区,关闭流等操作。

💤我们可以通过一个实验验证一下:在printf时我们先不用换行刷新缓冲区









所以说,缓冲区一定不在系统内部,而是库缓冲区,C语言提供。
2.2 异常退出

进程未完成预期功能,因外部信号或内部错误被迫中断,终止状态为「错误」。

💦 ctrl + c,信号终止

收到外部信号:

SIGINT(信号 2):用户在终端按下 Ctrl+C,终止前台运行的进程

SIGKILL(信号 9):强制终止进程,无法被捕获或忽略(命令:kill -9 进程PID),用于终止无响应的进程。

程序内部运行错误:进程执行过程中出现无法恢复的逻辑错误,触发内核终止信号,例如:

除零错误(int a = 1 / 0;);

栈溢出(递归调用无终止条件,耗尽栈空间);

自定义类型的非法操作(如未初始化的指针调用成员函数)。
注意:当进程发生异常退出,此时退出码无意义。

二、进程等待

1、进程等待的必要性

前面我们在进程状态中提到,当子进程结束,父进程不去获取子进程的退出信息,那么此时该子进程就会变成僵尸进程,操作系统依然需要维护相关的数据结构,就会造成内存泄漏。进程等待的一个重要作用就是回收子进程资源,防止内存泄漏,同时,获取子进程的退出信息



2、进程等待的方式

2.1 wait 方法

等待任意一个退出的子进程,并返回子进程pid。如果子进程一直不退出,父进程就会一直阻塞在wait 处。

#include<stdlib.h> #include<sys/wait.h> #include<sys/types.h> int main() { pid_t id = fork(); if(id == 0) // 子进程 { int cnt = 5; while(cnt--) { printf("我是一个子进程,我的pid:%d,ppid:%d\n",getpid(),getppid()); sleep(1); } exit(0); } sleep(10); pid_t rid = wait(NULL); if(rid > 0) printf("等待成功,%d\n",rid); return 0; } 

2.2 waitpid 方法

wait 的升级版,可以用来等待指定进程,这也是为什么fork 创建子进程时要给父进程返回子进程pid的原因。

参数pid 传 -1时等待任意进程,即此时与wait 完全相同。即wait(NULL) 与 waitpid(-1, NULL, 0) 作用完全一样。

利用 waitpid 进行阻塞等待指定的子进程:

#include<stdlib.h> #include<sys/wait.h> #include<sys/types.h> int main() { pid_t id = fork(); if(id == 0) // 子进程 { int cnt = 5; while(cnt--) { printf("我是一个子进程,我的pid:%d,ppid:%d\n",getpid(),getppid()); sleep(1); } exit(0); } sleep(10); pid_t rid = waitpid(id,NULL,0); if(rid > 0) printf("等待成功,%d\n",rid); return 0; } 
waitpid参数解析:
*status

子进程退状态,是一个输出型参数。这不就是我们前面提到的进程终止相关的信息吗。

#include<stdlib.h> #include<unistd.h> #include<sys/wait.h> #include<sys/types.h> #include<errno.h> int main() { pid_t id = fork(); if(id == 0) // 子进程 { int cnt = 3; while(cnt) { printf("我是一个子进程,我的pid:%d,ppid:%d\n",getpid(),getppid()); cnt--; } exit(1); } int status = 0; pid_t rid = waitpid(id, &status, 0); if(rid > 0) printf("等待成功,rid = %d, status = %d\n",rid, status); else printf("等待失败,%d : %s\n",errno, strerror(errno)); return 0; } 

❄️子进程明明是exit(1) 退出的,即退出码应该是1,但怎么打印出了256呢?

操作系统中用一个整数的低16个比特位(0~15)来存储进程终止的相关信息,高16位不用。低16个比特位又分为0~6记录终止信号(即进程异常退出信息)第7位为core dump标志(先不谈)以及8~15记录退出状态(即正常终止信息)

查看终止信号:

而我们的进程正常结束,所以0~7位均为0,第八位为1,即100000000,转化为10进制即256。

那我们对status 右移8不就是1了嘛,通过 (status >> 8) & 0xFF。就可以获得0~15个比特位。

❄️上面我们的程序正常终止,那进程异常终止呢?

此时子进程会把自己的异常终止信号放在低7个比特位,通过 status & 0x7F 就可以获得status的低7个比特位。



当我们在另外一个窗口杀掉子进程:kill -9 25130



可以看到我们拿到的status 的低7个比特位 即为9,而9 号信号就是我们在杀掉子进程时传递给子进程的终止信号,这也从侧面反映了进程异常终止其实就是收到了信号。



🔊我们还可以继续实验:在代码中写一个除零的操作





子进程异常终止,父进程等待成功,status 的低 7个比特位的值为8,对应8号信号(算术运算错误:除零错误)。
补充:

其实系统提供了两个宏来提取退出信息(底层也是位操作):

WIFEXITED(status):若为正常终止子进程返回的状态,则为真。(查看进程 是否是正常退出)

WEXITSTATUS(status):若WIFEXITED非零,提取子进程退出码。(查看进程 的退出码)
option:

(1)阻塞模式options = 0):父进程调用 waitpid() 后,若指定子进程未终止 / 未进入僵尸状态,父进程会被挂起(阻塞),直到子进程终止后才继续执行;

例如:scanf(),cin 等等,只要不进行输入,进程就一直会等待用户输入,阻塞。

当我们执行:sleep 100,此时输入指令无响应。



因为sleep 100 就是bash 进程的一个子进程,执行sleep 100,此时bash阻塞等待,所以,输入其他指令无响应。 

(2)非阻塞模式options = WNOHANG):父进程调用 waitpid() 后,无论指定子进程是否终止,都会立即返回,不会阻塞等待。

• 返回值大于0:表示等待成功;

• 返回值等于0:表示调用结束,子进程还没有退出;

• 返回值小于0:表示等待失败。
int main() { pid_t id = fork(); if(id == 0) { // 子进程 while(1) { printf("我是一个子进程,我的pid:%d,ppid:%d\n",getpid(),getppid()); sleep(1); } } // 父进程 while(1) // 非阻塞轮询(循环) { int status = 0; pid_t rid = waitpid(id, &status, WNOHANG); if(rid > 0) { printf("等待成功,rid = %d, exit code: %d, exit signal: %d\n",rid, WEXITSTATUS(status), WIFEXITED(status)); break; // 等待成功,结束 } else if(rid == 0) { printf("本轮调用结束,子进程还在运行\n"); sleep(1); // 执行任务... } else { printf("等待失败,%d : %s\n",errno, strerror(errno)); break; // 等待失败,结束 } } return 0; }
对于非阻塞调用,由于父进程并不知道子进程什么时候能退出,所以父进程需要一直去查看子进程退出信息(循环调用waitpid),即非阻塞轮询。同时,非阻塞调用父进程并不会一直阻塞等待,而是非阻塞轮询,所以父进程在等待过程中可以周期性地做一些自己的事情,这就提高了效率。
注意:进程异常终止时,退出码无意义!!!

就相当于你考试作弊被抓住,此时考试成绩就是无意义的。

3、进程等待怎么做到的?

首先说明一点:父进程没办法直接拿到子进程的信息

子进程退出后,变成僵尸进程,此时子进程的PCB(task_struct)仍被操作系统维护,而子进程的退出信息存储在子进程的 task_struct结构体对象 中,父进程无法直接拿到子进程退出信息。所以通过 waitpid 这样的系统调用接口间接获得。

前面提到的getpid(),getppid()也是这个原理。

😄 创作不易,你的点赞和关注都是对我莫大的鼓励,再次感谢您的观看😘

Read more

A2UI 技术原理深度解析:AI Agent 如何安全生成富交互 UI

本文深入解析 Google 开源的 A2UI 协议,探讨其核心架构、数据流设计以及为何它是 LLM 生成 UI 的最佳实践。 一、A2UI 是什么? A2UI (Agent-to-User Interface) 是 Google 于 2025 年开源的声明式 UI 协议。它解决了一个核心问题: 如何让 AI Agent 安全地跨信任边界发送富交互 UI? 传统的 Agent 交互往往是纯文本对话,效率低下。而直接让 LLM 生成 HTML/JS 代码又存在严重的安全风险。A2UI 提供了一个中间方案:Agent 发送声明式 JSON 描述 UI 意图,客户端使用自己的原生组件渲染。 安全性:

By Ne0inhk
2026年最新AI大模型学习路线(超详细,小白/程序员必收藏)从入门到精通!

2026年最新AI大模型学习路线(超详细,小白/程序员必收藏)从入门到精通!

当下AI大模型在人工智能领域的热度持续攀升,已然成为技术圈的核心风口,不仅吸引了大量行业从业者深耕,更有无数编程小白、转行人士想要入门掘金。但很多人面对繁杂的技术资料无从下手,不知道该从哪里开始、按什么顺序学习,踩了不少弯路。 今天就给大家整理了一份2026年最新、最系统的AI大模型学习路线,从0基础入门到精通实战,配套全套学习资源,不管你是纯小白还是有一定基础的程序员,跟着学就能少走弯路、快速上手,建议收藏备用,避免后续找不到! 1、大模型学习路线 2、从0到进阶大模型学习视频教程 从入门到进阶这里都有,跟着老师学习事半功倍。 3、 入门必看大模型学习书籍&文档.pdf(书面上的技术书籍确实太多了,这些是我精选出来的,还有很多不在图里) 4、 AI大模型最新行业报告 2026最新行业报告,针对不同行业的现状、趋势、问题、机会等进行系统地调研和评估,以了解哪些行业更适合引入大模型的技术和应用,以及在哪些方面可以发挥大模型的优势。 5、面试试题/经验 【大厂 AI 岗位面经分享(107 道)】 【AI

By Ne0inhk

2026 年最新 7 款热门 AI 编程工具评测,实用不踩坑(Trae 领衔推荐)

随着 AI 技术与编程领域的深度融合,高效智能的 AI 编程工具已成为开发者提升效率、规避冗余工作的核心伙伴。本次我们精选 7 款国内外主流 AI 编程工具,结合实测体验,从功能实用性、适配场景、使用门槛等维度展开介绍,为不同需求的开发者提供真实可参考的选择,全程无冗余铺垫,直接进入核心推荐。 Trae(字节跳动旗下 AI 编程工具) 作为字节跳动深耕 AI 编程领域的核心产品,Trae 依托字节跳动自研 Seed 大模型,凭借零使用成本、出色的中文适配性和全面的基础功能,成为本次评测中最适合国内开发者的入门及日常开发工具。与同类工具相比,Trae 最大的优势是无门槛接入,个人版完全免费,无需订阅付费即可使用全部核心功能,极大降低了开发者的使用成本,尤其适合预算有限的新手和个人开发者。功能上,它全面支持 Python、Java、JavaScript、Go 等 20 + 主流编程语言,覆盖前端、

By Ne0inhk
$19.99 订阅值不值?Google AI Pro 全面评测以及订阅会员权益功能解析详情

$19.99 订阅值不值?Google AI Pro 全面评测以及订阅会员权益功能解析详情

从单一工具到代理生态:Google AI Pro 深度评测报告 写在前面:2025 年 11 月,这注定是 AI 发展史上的一个分水岭。当我们将目光聚焦在 Google 刚刚完成的消费者订阅服务重组时,会发现原来的 “Google One AI Premium” 已成历史,取而代之的是层级更分明、野心更大的 Google AI Pro 与 Google AI Ultra。 这不只是改个名字那么简单。这代表了 Google 战略重心的根本性位移:从卖“聊天机器人”的访问权,转向构建一个由“智能代理(Agents)”驱动的生产力生态。 本文将为你剥开营销术语的外衣,对 Google AI Pro($19.99/月)

By Ne0inhk