从零写 OS 内核-第八篇:fork 与 exec —— 让进程“繁衍”起来!

“一个进程只能孤独运行?那不是操作系统,那是单机游戏。 今天,我们赋予进程‘生育’能力——通过 fork 复制自己,通过 exec 蜕变为新程序!”

在上一篇中,我们成功让内核运行了 第一个用户态程序 ,并通过系统调用实现安全交互。 但这还不够:真正的操作系统必须能 动态创建、管理和回收进程 ,支持 shell 启动任意命令,构建进程树。

而这一切的核心,就是 Unix 哲学的两大基石: ✅ fork() —— 复制当前进程 ✅ exec() —— 用新程序替换当前进程

今天,我们就来亲手实现这两大系统调用,让你的 OS 拥有“繁衍”能力!


🧬 一、为什么需要 fork + exec?

你可能会问: 为什么不直接“加载新程序”?

因为 Unix 的设计哲学是:

“进程创建”与“程序加载”是两个独立操作。

经典流程:

优势:

灵活性


fork
后可修改环境(如重定向 stdin/stdout),再
exec
一致性

:所有进程创建都走同一套机制
简洁性


shell
实现极简(只需 fork + exec)

💡 没有 fork/exec,就没有现代 shell、管道、后台任务!


📦 二、进程控制块(PCB)升级

我们需要为每个进程增加生命周期管理字段:

🔑 关键新增 : pid , parent_pid , state , exit_code —— 进程关系与状态管理的基础。


🧪 三、实现 sys_fork:复制进程

fork() 的核心是 复制父进程的整个执行环境 ,但有两点特殊:

  1. 子进程返回 0,父进程返回子 PID
  2. 共享文件描述符(暂不实现),但内存独立

步骤:

  1. 分配新 PCB 和 PID
  2. 复制父进程页目录

    (逐页复制物理页)

  3. 复制内核栈和寄存器状态
  4. 设置返回值魔法

代码框架(简化版):

⚠️ 注意 :实际需在汇编中保存完整上下文,此处为简化逻辑。


🔄 四、实现 sys_exec:程序替换

exec() 不创建新进程,而是 用新程序覆盖当前进程的用户空间 。

步骤:

  1. 释放当前用户页表

    (保留内核部分)

  2. 加载新 ELF 到用户空间
  3. 设置新用户栈

    (压入
    argc
    ,
    argv
    , 环境变量)

  4. 设置新 EIP 为 ELF 入口

关键:构造用户栈布局

代码框架:

✅ exec 成功后,原进程的代码、数据、堆栈全部被替换!


⚰️ 五、僵尸进程与 sys_wait

当进程调用 exit() ,它不应立即释放 PCB—— 父进程可能需要获取其退出状态 。

流程:

  1. 子进程退出 → 进入
    ZOMBIE
    状态(保留 PCB,释放内存)
  2. 父进程调用
    wait()
    → 内核返回子进程 PID 和退出码
  3. 内核
    彻底释放子进程 PCB

sys_wait 实现:

🛡️ copy_to_user :将内核数据安全复制到用户空间指针。


🧪 六、测试:用户态 shell 雏形

user_init.c (PID=1 的 init 进程):

运行效果:

✅ 你的 OS 现在能动态运行任意用户程序了!


⚠️ 七、优化方向(未来工作)

  1. 写时复制(Copy-on-Write, CoW) → fork 时不复制物理页,只设为只读,写时才复制(大幅提升性能)
  2. vfork / posix_spawn → 针对 fork + exec 场景的优化
  3. 进程组与会话 → 支持 shell 作业控制(jobs, fg/bg)
  4. 信号传递 → Ctrl+C 杀死前台进程

💬 写在最后

fork 和 exec 看似简单, 却承载了 Unix 系统 组合、复用、简洁 的设计哲学。

今天你实现的这两行系统调用, 正是几十年来无数开发者构建复杂系统的基石。

🌟 进程不再孤独,操作系统从此生生不息。


📬 动手挑战 : 实现一个用户程序,调用 fork() 两次,创建两个子进程分别执行不同命令。 欢迎在评论区分享你的进程树!

👇 下一篇你想看: 时钟中断与抢占式调度 ,还是 虚拟文件系统(VFS)入门 ?


#操作系统 #内核开发 #fork #exec #进程管理 #系统调用 #Unix #从零开始


📢 彩蛋 :关注后回复关键词 “fork” ,获取:
完整
sys_fork
/
sys_exec
实现代码
进程 PCB 结构体详细注释
用户态 init 程序模板(含 argv 构造)

最后修改: 2026年7月1日

作者

评论

发表评论

您的邮箱地址不会被公开。