什么是Fork
在计算机编程(尤其是 Linux/Unix 系统)中,fork() 是一个极其特殊且核心的系统调用。它的字面意思是“分叉”,功能是让一个正在运行的程序(父进程)在原地克隆出一个完全一样的副本(子进程)。
通俗点说,fork() 就像是细胞分裂,或者孙悟空拔下一根毫毛变出了一个一模一样的自己。
1. 核心特点:一次调用,两次返回
这是 fork() 最神奇的地方。普通的函数调用一次只返回一个值,但 fork() 调用后,代码会从同一个点继续向下执行,但在父进程和子进程中返回的值不同:
- 在父进程中:返回子进程的 PID(进程 ID,一个大于 0 的整数)。这让父进程知道自己的“孩子”是谁。
- 在子进程中:返回 0。这让子进程意识到“我是被创造出来的副本”。
- 如果失败:返回 -1(通常是因为系统资源耗尽)。
2. 它们之间是什么关系?
虽然子进程是父进程的“克隆体”,但它们之间有如下规则:
内存复制:子进程会获得父进程的数据段、堆和栈的副本。这意味着在 fork() 的那一刻,它们两者的变量值是一模一样的。
- 写时复制 (Copy-on-Write):为了节省内存,Linux 并不是真的立刻拷贝所有内存。只有当其中一个进程尝试修改某个变量时,系统才会为该进程真正复制那块内存。
- 独立运行:一旦分裂完成,父子进程就是两个独立的个体。父进程改了变量 x,子进程里的 x 不受影响。它们由操作系统调度,谁先跑、谁后跑是不确定的。
3. 为什么代码里要用它?
使用 fork() 有两个典型的“套路”目的:
- 隔离风险:主程序(父进程)不想因为用户的错误输入而崩溃。它派出一个子进程去处理 gets() 输入,如果用户输入太长导致栈溢出崩溃,死掉的只是子进程,父进程依然活着,可以继续下一轮循环。
- **保持状态一致性:**每次循环 fork() 出来的子进程,其内存布局(包括基地址、Canary 随机数等)和父进程完全一致。在黑客攻击中,这意味着你可以利用多个子进程来“试探”同一个加密值。
给个例子:
PID 的全称是 Process ID,中文通常称“进程标识符”或“进程编号”
1 |
|
现在有两个一模一样的程序在同时运行,但它们的变量 pid 的值不同:
- 父进程 (PID 100):
- 此时 pid = 101(大于 0);进入 if (pid > 0) 分支。
- 打印:“我是父进程,我创造的子进程 PID 是 101”。
- 子进程 (PID 101):
- 此时 pid = 0;进入 else if (pid == 0) 分支。
- 打印:“我是子进程,我返回的值是 0”。
由于 fork() 之后两个进程都会继续向下执行,所以这句话会被**打印两次:**一次由父进程打印,一次由子进程打印
更新: 2026-04-07 12:57:37
原文: https://www.yuque.com/idcm/wnemg9/soiawuvalk3k8i8g