news 2026/4/8 3:10:21

【Linux】从 fork 到进程终止:写时拷贝细节与常见退出方式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Linux】从 fork 到进程终止:写时拷贝细节与常见退出方式

【Linux】从 fork 到进程终止:写时拷贝细节与常见退出方式

Linux 进程创建(fork)与终止(exit/kill)是操作系统中最核心、最常被考察的机制之一。
本文重点讲解fork 的写时拷贝(Copy-On-Write, COW)实现细节,以及进程终止的各种方式(正常/异常/强制),结合内核视角、常见陷阱与生产实践。

1. fork() 的本质与写时拷贝(COW)机制

1.1 fork() 做了什么?

fork()是 POSIX 标准中创建新进程的系统调用,返回值:

  • 子进程:返回 0
  • 父进程:返回子进程 PID(>0)
  • 失败:返回 -1(errno)

最关键的一点:子进程是父进程的几乎完整副本(包括代码段、数据段、堆、栈、打开的文件描述符、信号处理、环境变量等)。

传统实现(很早的 Unix)会直接复制整个地址空间 →极其昂贵(内存拷贝 + 时间)。

1.2 现代 Linux 如何优化?→ 写时拷贝(Copy-On-Write)

Linux(从很早版本开始)采用COW机制:

  1. fork 时

    • 不复制物理页面,而是复制页表(page table)
    • 子进程的页表项指向父进程相同的物理页面
    • 所有用户态可写页面(大多数数据/堆/栈)被标记为只读(read-only)(内核在页表中设置写保护位)
  2. 读操作:直接访问共享的物理页面 →零拷贝,速度极快

  3. 第一次写操作(任一进程):

    • 触发页面故障(page fault)
    • 内核检测到写保护 → 分配一个全新物理页面
    • 把原页面内容复制到新页面
    • 更新当前进程的页表 → 指向新页面(可写)
    • 父/子进程各自拥有独立的副本

一句话总结
fork 后父子共享物理内存,直到其中一方写时才真正复制该页

1.3 COW 的典型表现(代码演示)
#include<stdio.h>#include<unistd.h>#include<sys/wait.h>intglobal_var=100;// 数据段intmain(){intstack_var=200;// 栈pid_tpid=fork();if(pid==0){// 子进程printf("子进程: global=%d, stack=%d\n",global_var,stack_var);global_var=999;// 写 → 触发 COW(该页被复制)stack_var=888;printf("子进程修改后: global=%d, stack=%d\n",global_var,stack_var);}else{// 父进程wait(NULL);printf("父进程: global=%d, stack=%d\n",global_var,stack_var);// 父进程看到的仍是 100 和 200(COW 后子进程修改的是自己的副本)}return0;}

输出

子进程: global=100, stack=200 子进程修改后: global=999, stack=888 父进程: global=100, stack=200

结论:父子进程修改的是各自独立的副本,不互相影响

1.4 COW 的优点与代价

优点

  • fork 极快(只需复制页表 + 标记写保护)
  • 内存利用率高(大量 fork 后 exec 的场景,如 shell、Apache prefork 模式,几乎不额外耗内存)

代价 / 副作用

  • 写操作会触发页面复制 →写放大(尤其是大进程 fork 后频繁写内存时)
  • 多进程同时写同一页 → 每个进程都复制一份
  • 内存碎片可能增加

生产建议

  • 尽量 fork 后尽快 exec(经典用法:避免 COW 带来的写放大)
  • 高并发服务器避免 fork 模型 → 改用线程池 / 事件驱动 / 多进程预 fork + accept(2) 复用

2. 进程终止的常见方式

Linux 进程退出有多种路径,影响退出码资源释放信号处理atexit/on_exit等清理函数是否执行。

方式系统调用/信号是否调用 atexit()是否发 SIGCHLD是否可捕获典型退出码备注 / 适用场景
main 返回exit()返回值最正常退出
exit() / _exit()exit_group() / exit是 / 否参数exit 调用 atexit,_exit 不调用
pthread_exit()否(仅线程)仅退出当前线程,主线程仍存活
return from mainexit()返回值等价于 exit()
SIGTERM (kill -15)是(若 handler 调用 exit)可捕获通常 143优雅终止(默认行为)
SIGKILL (kill -9)不可捕获通常 137强制杀死(不可拦截)
SIGABRT (abort())否(核心转储)可捕获通常 134断言失败等
段错误等异常SIGSEGV 等可捕获通常 139核心转储

关键区别

  • exit():调用 atexit/on_exit 注册的清理函数 → 刷新 stdio 缓冲区 → 关闭打开的文件(fclose)→ 然后调用 _exit()
  • _exit():直接系统调用 exit,不做任何用户态清理(缓冲区可能丢失)
  • SIGTERM:默认行为是终止进程,但可以捕获并做清理(调用 exit())
  • SIGKILL:内核强制杀死,无法捕获、无法清理(脏数据可能残留)

退出码约定(Shell 中 $?):

  • 正常退出:0 ~ 255(用户定义)
  • 被信号杀死:128 + 信号编号
    • SIGTERM (15) → 143
    • SIGKILL (9) → 137
    • SIGSEGV (11) → 139

3. 生产中最常见的终止流程(推荐)

#include<stdio.h>#include<stdlib.h>#include<signal.h>#include<unistd.h>staticvoidcleanup(void){printf("atexit 清理:关闭文件、释放资源...\n");}staticvoidsigterm_handler(intsig){printf("收到 SIGTERM,进行优雅退出...\n");// 可以做:保存状态、通知其他进程、关闭连接等exit(EXIT_SUCCESS);// 或 _exit(0) 看需求}intmain(){atexit(cleanup);signal(SIGTERM,sigterm_handler);// signal(SIGINT, sigterm_handler); // Ctrl+Cprintf("进程运行中... PID=%d\n",getpid());while(1){sleep(1);}return0;}

测试

# 终端1./a.out# 终端2kill-TERM<pid># 优雅退出,调用 atexitkill-9<pid># 强制杀死,不调用 atexit

小结 & 面试/运维高频问题

  1. fork 后父子进程共享哪些资源?(文件描述符、mmap 共享内存等)
  2. COW 触发条件是什么?(第一次写)
  3. SIGTERM 和 SIGKILL 区别?(可捕获 vs 不可捕获)
  4. 为什么生产中常用 SIGTERM + 优雅退出?(避免数据丢失、脏关闭)
  5. 如何防止子进程变成僵尸(zombie)?(父进程 wait/waitpid)
  6. 多线程程序中主线程 exit() 会怎样?(整个进程退出)

想继续深入哪个方向?
A. vfork() vs fork() vs clone() 区别
B. 僵尸进程、孤儿进程的产生与处理
C. 多线程信号分发与 pthread_kill
D. 内核视角:task_struct → do_exit 流程
E. 生产事故案例(SIGKILL 导致数据损坏)

告诉我字母,我们继续深挖!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/5 9:23:21

Linux最小系统详解

Linux 最小系统详解 Linux 最小系统&#xff08;Minimal Linux System&#xff09;是指一个精简到极致的 Linux 操作系统环境&#xff0c;只包含启动、运行基本命令和最小化交互所需的核心组件。通常&#xff0c;它的大小只有几 MB&#xff0c;甚至更小&#xff0c;常用于嵌入…

作者头像 李华
网站建设 2026/4/6 1:12:53

HTML初学者看过来-CSS画0.5px线的妙招

HTML初学者看过来-CSS画0.5px线的妙招HTML初学者看过来-CSS画0.5px线的妙招天呐&#xff0c;谁能想到HTML和CSS这么玩CSS里的魔法-如何画出那条隐形般的0.5px线奇技淫巧之一-transform缩放法瞬间变细术之二-伪类加border-image方案代码界的艺术家-linear-gradient渐变绘制高清时…

作者头像 李华
网站建设 2026/3/31 19:46:21

Target(塔吉特)的验厂抽查是重要环节

Target&#xff08;塔吉特&#xff09;的验厂抽查&#xff08;Spotcheck&#xff09; 是其供应链监督体系中一个非常重要的环节&#xff0c;其特点是突然性、针对性和深入性。了解其运作逻辑和注意事项&#xff0c;对于已通过首次正式审核的供应商维持资格至关重要。Target 验厂…

作者头像 李华
网站建设 2026/4/6 12:11:24

视频转GIF怎么弄?GIF图片在线制作高清教程

做自媒体配图、电商主图、微信推文动图&#xff0c;或是学生做课件、职场做汇报素材时&#xff0c;常想把精彩视频片段做成GIF动图&#xff0c;却在视频转GIF版的GIF图片在线制作中踩坑&#xff1a;要么转换操作繁琐&#xff0c;新手不会裁剪片段、调节播放速度;要么转换后GIF画…

作者头像 李华
网站建设 2026/3/26 21:39:25

【dz-1044】基于单片机的自动门

基于单片机的自动门 摘要 在公共场所和家居环境中&#xff0c;自动门的便捷性与安全性已成为提升通行体验的重要因素。传统自动门常存在感应不灵敏、防夹功能缺失或调节不便等问题&#xff0c;可能导致通行效率低或安全隐患。尤其在人流密集场所&#xff0c;可靠的感应触发、及…

作者头像 李华
网站建设 2026/4/2 14:56:57

【dz-1047】基于单片机的手提文件箱防盗锁设计

摘要 在信息安全日益受到重视的当下&#xff0c;手提文件箱作为重要文件和物品的载体&#xff0c;其防盗性能的高低直接关系到信息与财产的安全。传统的机械锁具防盗方式&#xff0c;存在钥匙易丢失、被盗配&#xff0c;以及无法实时监控和远程预警等问题&#xff0c;难以满足…

作者头像 李华