【Linux系统篇】从 fork 到 WNOHANG:进程创建与等待机制详解
个人主页爱和冰阔乐专栏传送门《数据结构与算法》 、C学习方向C方向学习爱好者⭐人生格言得知坦然 失之淡然博主简介文章目录前言一、进程创建1.1 fork函数初识1.2 写时拷贝1.3 fork创建子进程的用途1.4 fork失败的原因二、进程终止2.1 进程退出的三种场景2.2 main函数的返回值2.3 strerror和errno2.4 异常退出要看信号2.5 return、exit与_exit三、进程等待3.1 为什么需要进程等待3.2 wait等待子进程3.3 waitpid等待指定子进程四、status如何保存退出信息4.1 为什么exit(1)打印成2564.2 不要在业务代码里手写位解析4.3 异常退出看终止信号4.4 使用系统宏读取退出结果五、非阻塞等待5.1 WNOHANG的返回值5.2 非阻塞等待代码5.3 等待期间处理其他任务总结前言进程控制这一部分主要讲三件事怎么创建子进程、进程怎么退出、父进程怎么回收子进程。fork创建子进程以后父子进程会各自执行。子进程完成任务后可能正常返回也可能因为异常收到信号。父进程需要通过wait或waitpid获取结果并回收资源。本文仍按学习时的顺序展开从fork、写时拷贝讲到退出码、进程等待和非阻塞等待。一、进程创建1.1 fork函数初识Linux中使用fork从当前进程创建一个子进程#includeunistd.hpid_tfork(void);fork的返回值要结合执行位置判断执行位置返回值用途子进程0进入子进程分支父进程子进程PID记录并管理新建的子进程创建失败-1根据错误信息处理失败父进程返回子进程PID是为了后续能够指定等待和管理这个子进程子进程返回0则可以直接作为分支判断标记。一个函数看起来返回了两次是因为fork之后出现了父子两个执行流。fork之前只有父进程执行fork之后父子进程都从调用位置继续向后运行谁先执行由调度器决定。进入内核后OS需要为子进程分配内核数据结构、复制父进程的部分进程信息、把子进程加入进程列表最后再让调度器选择执行。1.2 写时拷贝创建子进程时父子进程可以共享只读代码。数据需要保持独立但操作系统不会在fork时立刻复制全部数据页。刚创建完成时父子页表先指向同一批物理数据页并把相关页面设置成写时拷贝状态。某一方真正写入时CPU触发缺页异常。OS确认虚拟地址合法、页表映射正常而且这次访问属于写时拷贝后再申请新的物理页、复制数据并修改页表映射。因此准确说法是写时拷贝不是“写实拷贝”。它不是普通程序错误而是OS利用页权限实现延迟复制。为什么要写时拷贝如果fork时把父进程数据完整复制一份数据越多创建子进程越慢。子进程通常只修改少量数据全部复制会让大量未修改内容在内存中重复保存。写时拷贝把“创建时全部复制”改成“真正修改时再复制”减少了创建时间也提高了物理内存利用率。结论fork时不立即复制全部数据真正写入时才复制对应物理页。1.3 fork创建子进程的用途子进程被创建出来一定是为了完成某个任务。常见用途有两类子进程继续执行当前程序通过if/else分流让父子进程完成不同任务子进程调用exec系列接口执行全新的程序。Shell运行外部命令时通常就是先创建子进程再让子进程替换成目标程序。1.4 fork失败的原因fork失败时返回-1。常见原因是系统资源不足例如无法继续分配内核数据结构、达到进程数量限制或内存压力过大。失败后不能继续把返回值当成父进程分支处理。二、进程终止进程终止的本质是OS释放该进程占用的内核数据结构、代码和数据等资源。2.1 进程退出的三种场景代码运行完毕结果正确代码运行完毕但结果不正确代码异常终止例如除零、野指针等情况。判断顺序正常退出看退出码异常退出看终止信号。子进程也是进程执行结果同样分为这三种情况。子进程被创建是为了完成任务父进程需要知道任务是否完成、结果是否正确以及有没有异常退出。2.2 main函数的返回值写C/C代码时我们经常在main末尾写return 0。这个返回值给谁它又表示什么main是程序入口。return 0通常表示程序执行成功非0表示任务失败。不同非0值可以对应不同错误原因具体含义由程序自己约定。只有main有“执行到函数末尾等价于返回0”的特殊规则。普通的非void函数如果没有返回值不能简单理解成默认返回0。子进程没有打印内容时bash怎么判断它的执行情况先看下面的例子#includestdio.hintmain(){// printf(hello world\n);FILE*fpfopen(log.txt,r);if(fpNULL)return1;fclose(fp);return0;}这里使用echo $?查看最近一个前台进程的退出码。进程退出后内核会保留退出状态等待父进程读取。2.3 strerror和errno怎样把错误编号转换成文字描述C标准库提供了strerror可以先查看man 3 strerror。#includestdio.h#includestring.hintmain(){inti0;//并不清楚多少个随机写的200for(;i200;i){printf(%d-%s\n,i,strerror(i));}}打印结果能看到当前系统支持的错误描述。具体数量与系统环境有关不能把某台机器的结果当成所有Linux系统的固定值。系统调用失败时通常会设置errno。程序可以根据自己的约定返回对应值但Shell退出状态通常只保留低8位不适合直接承载任意大的错误编号。退出码由程序自己约定不要求必须对应C标准库错误码。例如程序可以使用13表示某个自定义错误。退出码只能描述正常结束后的执行结果。若进程异常终止还要继续看终止信号。2.4 异常退出要看信号下面先看一个异常退出场景信号部分后续再详细介绍#includestdio.hintmain(){inta10;a/0;return89;}进程在执行return 89之前已经异常终止所以89没有机会成为退出码。异常退出通常表示进程收到了信号。2.5 return、exit与_exit进程退出常见有下面几种方式在main中执行return主函数结束随后进程退出。普通函数中的return只结束当前函数在代码任意位置调用exit(code)整个进程直接结束。// 在任意函数中结束整个进程exit(23);#includestdio.h#includestring.h#includeerrno.h#includestdlib.hvoidfun(){printf(fun begin!\n);exit(40);printf(fun end!\n);}intmain(){fun();printf(man!\n);}任何位置调用exit都会结束整个进程这与普通函数中的return不同。退出状态由内核保存父进程通过等待接口读取。_exit()直接终止调用它的进程。exit是C标准库接口_exit是系统调用层接口。下面比较两者的差异。调用exit时若printf没有换行内容可能先留在stdio缓冲区。进程退出前exit会刷新缓冲区因此最终仍能看到输出。若字符串带换行在终端环境下通常会提前刷新。#includestdio.h#includeunistd.h#includestdlib.hintmain(){printf(man\n);sleep(2);exit(23);}把同样代码改成_exit后带换行时可能已经触发终端行缓冲刷新不带换行时内容仍留在用户态缓冲区_exit不负责刷新所以看不到对应输出。exit会执行C库清理并刷新stdio缓冲区_exit不会执行这些用户态清理直接进入进程终止流程。exit在进入_exit之前通常还会调用通过atexit注册的清理函数并关闭标准I/O流。因此这里观察到的是C库缓冲区差异。若缓冲区完全由内核维护调用_exit时就不应该出现这种现象。方式作用范围是否执行C库清理return结束当前函数在main中会结束进程由main返回时正常完成exit(code)结束整个进程是会刷新stdio缓冲区_exit(code)直接终止整个进程否不刷新stdio缓冲区三、进程等待3.1 为什么需要进程等待子进程退出后父进程如果一直不处理就可能形成僵尸进程。僵尸进程已经结束kill -9也无法再次终止它父进程需要知道子进程任务是否完成、结果是否正确父进程通过等待回收子进程资源并按需取得退出信息。3.2 wait等待子进程父进程创建子进程后需要通过wait或waitpid等接口等待子进程。这个过程就是进程等待。下面就是僵尸进程的代码#includestdio.h#includeerrno.h#includestring.h#includestdlib.h#includeunistd.h#includesys/types.hintmain(){pid_tidfork();if(id0){//子intcnt5;while(cnt){printf(我是一个子进程pid:%d,ppid:%d\n,getpid(),getppid());sleep(1);cnt--;}exit(0);}//父进程sleep(100);return0;}pid_t wait(int *status)等待任意一个子进程。成功时返回被回收子进程的PID并把退出信息写入status失败时返回-1。不关心退出信息时可以传NULL。#includestdio.h#includeerrno.h#includestring.h#includestdlib.h#includeunistd.h#includesys/types.h#includesys/wait.hintmain(){pid_tidfork();if(id0){//子intcnt5;while(cnt){printf(我是一个子进程pid:%d,ppid:%d\n,getpid(),getppid());sleep(1);cnt--;}exit(0);}//父进程// sleep(100);// 情况// 1.子进程退出wait回收解决僵尸进程// 2.五秒以内子进程没有退出那么wait在干嘛sleep(10);pid_tridwait(NULL);if(rid0)printf(wait success,rid:%d\n,rid);//rid子进程pidsleep(10);return0;}可以使用下面的Shell循环持续监测。父进程执行wait后退出的子进程不会继续保持僵尸状态while:;dopsajx|head-1psajx|grep[c]odesleep1done现象如果等待子进程子进程没有退出父进程会阻塞在wait调用处这里与scanf场景类似这个例子可以按时间看前5秒父子进程同时运行子进程退出后在父进程调用wait前短暂进入僵尸状态wait返回后子进程被回收。3.3 waitpid等待指定子进程waitpid同样用于等待子进程。成功时返回子进程PIDstatus是输出型参数options控制阻塞方式。传入指定PID时父进程只等待对应子进程。// 等待任意子进程pid_tridwaitpid(-1,NULL,0);// 等待指定子进程pid_tridwaitpid(id,NULL,0);父进程没有子进程、传入PID不属于当前进程或参数错误时waitpid会失败。四、status如何保存退出信息4.1 为什么exit(1)打印成256父进程通过status取得子进程退出信息。它不只保存main的返回值还要同时表示进程是否正常退出、终止信号等状态。不要直接把原始status当退出码。先判断退出类型再提取对应信息。intstatus0;pid_tridwaitpid(id,status,0);if(rid0){printf(raw status: %d\n,status);}这里设置的退出码是1但原始status打印成256这是为什么status不是单纯的退出码它同时编码正常退出、退出码、终止信号和core dump等信息。常见实现中退出码位于较高的8位所以exit(1)对应的原始值会表现为1 8也就是256。4.2 不要在业务代码里手写位解析右移8位可以帮助理解常见布局但正式代码应优先使用sys/wait.h提供的宏。if(WIFEXITED(status)){printf(exit code: %d\n,WEXITSTATUS(status));}elseif(WIFSIGNALED(status)){printf(signal: %d\n,WTERMSIG(status));}这时就能解释bash为什么能拿到退出码。父子进程地址空间彼此独立子进程修改全局变量会发生写时拷贝父进程不能靠普通变量直接读取结果只能通过OS提供的等待接口取得。4.3 异常退出看终止信号Linux中有多种信号可以使用kill -l查看。前面使用过kill -9。列表左侧是信号编号右侧是信号名称这些名称在头文件中以宏定义形式存在。grep#define SIGKILL/usr/include/*-Rvim/usr/include/asm/signa信号列表中没有0号终止信号。进程异常终止时status会记录对应的终止信号。若进程正常退出终止信号部分为0若进程被信号终止退出码就没有业务意义。手工执行status 0x7F可以帮助理解但正式代码应使用WIFSIGNALED和WTERMSIG。由于进程具有独立性父进程必须通过系统调用取得子进程退出信息。wait和waitpid从哪里取得信息子进程退出后会释放大部分资源但内核仍保留必要的进程结构和退出状态等待父进程读取。4.4 使用系统宏读取退出结果OS不希望用户在业务代码里反复手写位运算因此提供了对应宏WEXITSTATUS获取正常退出码WIFEXITED判断进程是否正常退出WIFSIGNALED判断进程是否被信号终止WTERMSIG获取终止信号。五、非阻塞等待5.1 WNOHANG的返回值waitpid的最后一个参数有多个选项。这里重点看WNOHANG子进程尚未结束时立即返回不阻塞父进程。非阻塞等待需要配合轮询。等待方可以在子进程运行期间继续处理自己的任务。waitpid使用WNOHANG时可以按返回值区分三种结果返回值含义 0已回收对应子进程返回值就是子进程PID 0子进程仍在运行本次立即返回 0等待失败需要检查参数和错误信息5.2 非阻塞等待代码下面看非阻塞等待的完整判断while(1){intstatus0;pid_tridwaitpid(id,status,WNOHANG);if(rid0){if(WIFEXITED(status)){printf(exit code: %d\n,WEXITSTATUS(status));}break;}if(rid0){printf(子进程还在运行\n);sleep(1);continue;}perror(waitpid);break;}5.3 等待期间处理其他任务父进程在waitpid返回0时可以周期性执行自己的任务if(rid0){Download();Log();sleep(1);}总结fork通过写时拷贝降低创建子进程的成本return、exit和_exit决定进程如何结束wait与waitpid负责获取退出结果并回收子进程。正常退出先看退出码异常退出先看终止信号。解析status时优先使用系统提供的宏不要在业务代码里依赖手写位运算。使用WNOHANG后父进程可以在等待期间继续处理任务但轮询需要控制频率不能写成一直占用CPU的空转循环。资源分享【Linux进程】程序地址空间详解虚拟地址、页表、写时拷贝与mm_struct【Linux排障实战】Docker容器启动失败怎么查端口、日志、权限与网络【Linux系统编程】环境变量深度解析——从 fork 继承到 export 内建命令两张表打通进程上下文

相关新闻

本科毕业设计全流程问题解析与应对策略

本科毕业设计全流程问题解析与应对策略

1. 本科毕业设计常见问题深度解析毕业季来临,又到了大四学子们为毕业设计焦头烂额的时候。作为指导过多届毕业设计的"老司机",我整理了一份超详细的毕业设计问答指南,希望能帮助大家少走弯路。这份问答不是那种泛泛而谈的"正确…

2026/7/23 21:03:27阅读更多 →
长效缓释载体基石!DSPE-PEG2k-OH 专用长循环脂质体构建原料

长效缓释载体基石!DSPE-PEG2k-OH 专用长循环脂质体构建原料

脂质体是经典纳米递送载体,兼具优异的生物膜仿生特性、灵活的配方可调性与高效的药物包封能力,广泛应用于功能纳米材料、脂质递送系统及生物医用材料领域。 但常规脂质体存在明显短板:储存与使用阶段易受颗粒团聚、界面相互作用、外界环境扰动…

2026/7/23 21:01:27阅读更多 →
信创环境下 RPA 替代选型:国产 Agent 兼容能力与迁移成本分析 —— 企业级自动化升级全指南

信创环境下 RPA 替代选型:国产 Agent 兼容能力与迁移成本分析 —— 企业级自动化升级全指南

在2026年数字化转型步入深水区的背景下,企业对于智能自动化技术的选型已从早期的“功能尝鲜”转向“生产级可靠性”与“信创兼容性”的双重追求。随着信创建设从办公外围系统深入至核心业务领域,传统基于Windows架构的RPA(机器人流程自动化&a…

2026/7/23 21:01:27阅读更多 →
AI数字人形象定制如何做到“一秒辨真伪”?揭秘头部平台未公开的8项微表情校准指标

AI数字人形象定制如何做到“一秒辨真伪”?揭秘头部平台未公开的8项微表情校准指标

更多请点击: https://intelliparadigm.com 第一章:AI数字人形象定制如何做到“一秒辨真伪”? 在高保真AI数字人构建中,“一秒辨真伪”并非追求绝对不可分辨,而是通过多维度感知一致性实现人类视觉与认知系统的瞬时信任…

2026/7/23 22:29:40阅读更多 →
【AI数字人直播变现实战手册】:0基础7天打造24小时自动带货直播间

【AI数字人直播变现实战手册】:0基础7天打造24小时自动带货直播间

更多请点击: https://intelliparadigm.com 第一章:AI数字人直播变现的核心逻辑与商业闭环 AI数字人直播并非简单地将真人主播替换成虚拟形象,其本质是构建以“低成本、高复用、强可控”为特征的自动化内容生产与用户价值转化系统。核心逻辑在…

2026/7/23 22:29:40阅读更多 →
复现论文代码反复报错?一套基于原文对照与变量溯源的实验 Debug 方法论(含工具选型)

复现论文代码反复报错?一套基于原文对照与变量溯源的实验 Debug 方法论(含工具选型)

文章目录多维度对比:Debug 方案谁更适合学术实验靠岸学术 Scholaread:把论文精读变成 Debug 的诊断工具核心功能与 Debug 场景适配实际使用场景其他 Debug 方案简评断点调试 结构化日志ChatGPT / Claude 逐段问诊对照原文手动排查GitHub Issues Papers…

2026/7/23 22:29:40阅读更多 →
用 WorkBuddy 批量整理文件时,怎样避免“做完了却不能交付”?

用 WorkBuddy 批量整理文件时,怎样避免“做完了却不能交付”?

用 WorkBuddy 批量整理文件时,怎样避免“做完了却不能交付”? 先把任务写成可验收的文件契约:哪些文件可以读、哪些不能动,按什么字段分类和重命名,结果写到哪个新目录,遇到重复名、缺字段或损坏文件如何处…

2026/7/23 22:29:40阅读更多 →
2026年英文论文翻译工具横评:5款主流方案实测对比与效率选型指南

2026年英文论文翻译工具横评:5款主流方案实测对比与效率选型指南

文章目录多维度对比:一张表看清差异靠岸学术 Scholaread:翻译精读引用一站打通核心功能一览实际使用场景其他主流方案简评沉浸式翻译(Immersive Translate)知云文献翻译DeepL沙拉查词(Saladict)欧路词典常见…

2026/7/23 22:29:40阅读更多 →
网站自动建站备份工具:本地测试站,1分钟平滑传阿里云

网站自动建站备份工具:本地测试站,1分钟平滑传阿里云

普通办公电脑上的网页文件传到云端服务器,过去靠手动上传。现在用软件打包。本地运行环境装有PHP 8.1版本的电脑里,存放着500MB的网页图片。传到拥有2核4G配置的阿里云轻量应用服务器,整个动作在60秒内完成。文件传输速度能达到每秒15MB。普通…

2026/7/23 22:27:40阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/23 0:56:31阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/23 0:56:31阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/23 0:56:31阅读更多 →
Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具 【免费下载链接】chitchatter Secure peer-to-peer chat that is serverless, decentralized, and ephemeral 项目地址: https://gitcode.com/gh_mirrors/ch/chitchatter Chitchatter是一款革命性的安…

2026/7/23 0:00:28阅读更多 →
从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

更多请点击: https://intelliparadigm.com 第一章:从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表) 当AI副业主理人不再仅满足于单次服务交付,而是主动构建可复用、可裂变、可…

2026/7/23 0:00:28阅读更多 →
油泥处理设备哪里能买到

油泥处理设备哪里能买到

油泥处理设备哪里有?这是许多从事油田、炼化、清罐业务的从业者最关心的问题。根据河南三丰环保设备有限公司的行业经验,选购油泥处理设备的核心在于设备能否适配当地环保法规与原料特性,而非单纯看价格。该公司总经理王钦田先生指出&#xf…

2026/7/23 0:00:28阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/22 22:56:18阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/23 18:58:18阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/23 18:58:18阅读更多 →