0

0

Linux进程生命周期管理_创建运行退出流程解析【教程】

冷炫風刃

冷炫風刃

发布时间:2026-01-08 12:38:43

|

346人浏览过

|

来源于php中文网

原创

fork()复制父进程生成子进程,子进程返回0、父进程返回PID;实际采用写时复制降低开销;需wait()回收僵尸进程;子进程应优先用_exit()避免缓冲区问题。

linux进程生命周期管理_创建运行退出流程解析【教程】

进程是怎么被 fork() 出来的

Linux 中新进程几乎都源于 fork() 系统调用,它会复制当前进程的地址空间、文件描述符、信号处理等状态,生成一个几乎完全相同的子进程。注意:子进程从 fork() 返回值为 0,父进程返回子进程 PID(正整数),出错则返回 -1

常见误区是认为 fork() 后父子进程执行顺序确定——实际由调度器决定,无先后保障。若需同步,必须显式使用 wait() 或信号机制。

  • fork() 不加载新程序,只是复制;要运行不同代码得紧接着调用 execve() 类函数
  • 写时复制(Copy-on-Write)让 fork() 实际开销远小于内存大小所暗示的那样
  • 频繁 fork() 但不 exec(如某些守护进程模型)可能因页表膨胀引发性能抖动

execve() 替换进程映像的关键细节

execve() 不创建新进程,而是用指定程序完全替换当前进程的用户空间代码、数据、堆和文件描述符表(除非标记了 CLOEXEC)。调用成功后,原进程“变成”新程序,execve() 永远不会返回;失败才返回 -1

容易踩的坑:

  • 传入的 argv 数组必须以 NULL 结尾,否则 execve() 可能读越界并失败(错误码 EFAULT
  • 路径必须绝对或相对于当前工作目录;用 execvpe() 可自动查 $PATH,但需确保环境变量有效
  • 若目标程序是脚本且无 #! 解释器声明,内核会直接报 ENOEXEC

子进程退出后,父进程不 wait() 会发生什么

子进程终止后进入“僵尸状态(Zombie)”,内核保留其退出状态、PID 和少量元数据,直到父进程调用 wait()waitpid() 或类似接口回收。不回收会导致:ps 显示状态为 Z,PID 无法复用,长期积累可能耗尽进程表。

AI-Text-Classifier
AI-Text-Classifier

OpenAI官方出品,可以区分人工智能书写的文本和人类书写的文本

下载

典型应对方式:

  • 父进程主动调用 waitpid(-1, &status, WNOHANG) 非阻塞轮询
  • 注册 SIGCHLD 信号处理器,在其中调用 waitpid() 回收(注意信号安全函数限制)
  • 对完全不关心子进程结果的场景,可提前设 signal(SIGCHLD, SIG_IGN),内核将自动清理僵尸进程(仅限 Linux 2.6+)

为什么 exit()_exit() 不能混用

exit() 是 C 库函数,会刷新 stdio 缓冲区、调用 atexit() 注册的函数、关闭打开的流;_exit() 是系统调用封装,立即终止进程,不执行任何清理。

fork() 后的子进程中,若已调用 execve(),用哪个都行;但若没 exec,直接 exit() 可能导致父进程的 stdio 缓冲区被重复刷新(父子共享 FILE 结构体副本,底层 fd 相同)。

稳妥做法:

  • 子进程 fork() 后立即 _exit(),避免 stdio 干扰
  • 主流程或 exec 后的进程可用 exit()
  • 在信号处理中只能用 _exit(),因 exit() 不是异步信号安全函数
pid_t pid = fork();
if (pid == 0) {
    // 子进程
    execve("/bin/ls", (char*[]){ "ls", "-l", NULL }, environ);
    _exit(127); // execve 失败,必须用 _exit
} else if (pid > 0) {
    int status;
    waitpid(pid, &status, 0); // 父进程等待回收
} else {
    perror("fork");
}
进程生命周期里最易被忽略的是资源归属边界:文件描述符是否继承、信号处置是否重置、stdio 缓冲是否隐式共享——这些不体现在代码行数里,却直接决定程序在长时间运行或高并发下的稳定性。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

231

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

435

2024.03.01

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

194

2025.06.09

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

186

2025.07.04

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1006

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

56

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

346

2025.12.29

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

382

2023.07.18

Golang 分布式缓存与高可用架构
Golang 分布式缓存与高可用架构

本专题系统讲解 Golang 在分布式缓存与高可用系统中的应用,涵盖缓存设计原理、Redis/Etcd集成、数据一致性与过期策略、分布式锁、缓存穿透/雪崩/击穿解决方案,以及高可用架构设计。通过实战案例,帮助开发者掌握 如何使用 Go 构建稳定、高性能的分布式缓存系统,提升大型系统的响应速度与可靠性。

27

2026.01.09

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL 教程
PostgreSQL 教程

共48课时 | 6.8万人学习

Git 教程
Git 教程

共21课时 | 2.5万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号