一、fork 函数:从'会用'到'懂原理'的再认识
1. fork 简单介绍
Linux 中创建子进程的核心系统调用,以父进程为模板创建独立子进程,调用一次、返回两次。
#include <unistd.h>
pid_t fork(void);
返回值:自进程中返回 0,父进程返回子进程 id,出错返回 -1。
| 返回值 | 所属进程 | 含义说明 |
|---|---|---|
| 0 | 子进程 | 子进程成功创建,返回 0 作为'标识',子进程可通过 getppid() 获取父进程 PID |
| 大于 0 | 父进程 | 返回值为新创建子进程的 PID(进程唯一标识),父进程可通过该值管理子进程 |
| -1 | 父进程 | 子进程创建失败,errno 会被设置为对应错误码(如 EAGAIN 表示资源不足、ENOMEM 表示内存不足) |
2. fork 的内核执行流程
当进程调用 fork(),控制流进入内核后,内核会完成以下步骤:
1. 资源分配:为新创建的子进程分配独立的内存空间与内核数据结构(如进程控制块 PCB);
当内核为子进程创建 PCB 时,会立刻完成以下关键字段的初始化(无需拷贝父进程数据):
**PID:**内核从可用的进程 ID 池中为子进程分配唯一的 PID(绝对不会和父进程/其他进程重复);
**PPID:**直接设置为父进程的 PID(明确父子关系,这是进程树的核心);
**进程状态:**初始化为'就绪态'(TASK_RUNNING),等待调度器分配 CPU;
基本权限/优先级:继承父进程的基础优先级,但会标记为独立的进程实体。
这些信息是子进程的'身份标识',必须在创建 PCB 时就确定 —— 否则操作系统根本无法区分'这个空 PCB 属于谁',也无法把它加入进程列表进行管理。
2. 数据拷贝:将父进程的部分核心数据结构(如 PCB 中的进程信息)拷贝至子进程;
在完成资源分配后,内核会将父进程的核心数据结构(如 PCB 中的进程属性信息)拷贝至子进程,而父进程的非核心标识类数据,会通过以下方式完成继承:
- 文件描述符表、信号处理方式、环境变量 → 直接拷贝到子进程 PCB;
- 代码段、数据段、堆/栈 → 基于写时拷贝共享(仅标记只读,不立即拷贝数据);
- 页表 → 初始化后指向父进程的物理内存页(写时拷贝的基础)。
3. 进程注册:将子进程添加到系统的进程列表中,使其成为可被调度的进程;
4. 返回与调度:fork() 系统调用返回,子进程进入操作系统调度器的调度队列,等待被分配 CPU 资源。

3. 写时拷贝机制
通常情况下,父子进程的代码段是共享的;若父子进程都不修改数据,数据也会保持共享状态。而当任意一方尝试修改数据时,系统会通过写时拷贝的方式,为修改方单独复制一份数据副本。具体过程可参考下图:

















