解读Linux中的fork机制
俞事 发布于 阅读:191
Linux中的fork机制是进程创建的核心系统调用,通过复制现有进程生成新进程。父进程调用fork后,内核执行以下步骤:
-
进程控制块复制
内核创建新的task_struct结构体,复制父进程的PCB信息,包括:- 进程状态
- 打开的文件描述符表
- 信号处理程序
- 虚拟内存映射
- 当前工作目录
-
写时复制优化(COW)
物理内存页不会立即复制,父子进程共享同一内存空间。内核将内存页标记为只读:- 当任一进程尝试写入共享页时触发页错误
- 内核为该进程分配新物理页并复制内容
- 修改页表项指向新物理页
-
资源分配
- 分配新的PID给子进程
- 继承父进程的文件描述符表(相同文件打开偏移)
- 复制父进程的页表项
- 创建独立的进程内核栈
-
返回值处理
系统调用返回两次:- 父进程接收子进程PID
- 子进程接收0返回值
- 返回-1表示创建失败(通过检查errno确定错误类型)
-
执行差异处理
内核设置子进程特定属性:- 清除挂起信号队列
- 重置统计信息(如CPU使用时间)
- 不继承父进程的内存锁(mlock)
- 清除跟踪标记(ptrace相关)
-
与线程创建的差异
对比pthread_create:- 线程共享地址空间、文件描述符等资源
- fork产生的进程拥有独立地址空间
- 线程创建不涉及COW机制
-
vfork变体
特殊场景优化版本:- 子进程共享父进程地址空间
- 阻塞父进程直到子进程调用exec或exit
- 避免COW带来的开销
- 直接操作父进程堆栈存在安全隐患
-
执行后续操作
典型使用模式:
if (pid == 0) {
// 子进程执行exec替换镜像
execve(...);
} else if (pid > 0) {
// 父进程可能调用waitpid
} else {
// 错误处理
} -
性能影响因子
创建开销取决于:- 进程地址空间大小
- 页表项数量
- 共享内存区域数量
- 挂载的命名空间数量
- SECCOMP过滤器复杂度
-
现代扩展机制
补充方案:- clone系统调用:更细粒度控制资源共享
- posix_spawn:组合fork+exec的优化接口
- PID命名空间:容器环境中的隔离fork行为