fork()

fork()

2024 南京大学《操作系统:设计与实现》
fork()

创建状态机

pid_t fork(void);

现在我们已经有 “一个状态机” 了

  • 只需要 “创建状态机” 的 API 即可
  • UNIX 的答案: fork
    • 做一份状态机完整的复制 (内存、寄存器现场)

center

2024 南京大学《操作系统:设计与实现》
fork()

fork() 的行为

立即复制状态机

  • 包括所有信息的完整拷贝
    • 每一个字节的内存
    • 打开的文件 (共享)
    • ……
    • 复制失败返回 -1
      • errno 会返回错误原因 (man fork)

如何区分两个状态机?

  • 新创建进程返回 0
  • 执行 fork 的进程返回子进程的进程号
2024 南京大学《操作系统:设计与实现》
fork()

Fork Bomb

新建状态机需要资源

  • 只要不停地创建进程,系统还是会挂掉的
  • Don't try it (or try it in docker)
    • 你们交这个到 Online Judge 是不会挂的

center

2024 南京大学《操作系统:设计与实现》
fork()

代码解析: Fork Bomb

:(){:|:&};:   # 刚才的一行版本

:() {         # 格式化一下
  : | : &
}; :

f() {      # bash: 允许冒号作为 identifier
  f | f &
}
f
  • 类比原子弹:一个重原子核 (U-235/Pu-239) 被中子击中后分裂成两个较轻的原子核,同时释放出能量和更多的中子
    • “自我增殖”
2024 南京大学《操作系统:设计与实现》
fork()

这次你们记住 Fork 了!

进程总有 “被创建” 的关系

  • 因此总能找到 “父子关系”
  • 因此有了进程树 (pstree)
systemd-+-ModemManager---2*[{ModemManager}]
        |-NetworkManager---2*[{NetworkManager}]
        |-accounts-daemon---2*[{accounts-daemon}]
        |-at-spi-bus-laun-+-dbus-daemon
        |                 `-3*[{at-spi-bus-laun}]
        |-at-spi2-registr---2*[{at-spi2-registr}]
        |-atd
        |-avahi-daemon---avahi-daemon
        |-colord---2*[{colord}]
        ...
2024 南京大学《操作系统:设计与实现》
fork()

理解 fork: 习题 (1)

阅读程序,写出运行结果

pid_t x = fork();
pid_t y = fork();
printf("%d %d\n", x, y);

一些重要问题

  • 到底创建了几个状态机?
  • pid 分别是多少?
    • “状态机视角” 帮助我们严格理解

我的偷懒 (出期末考试题) 方法

  • 拍脑袋 → 估算难度 → 用 model checker 跑结果
2024 南京大学《操作系统:设计与实现》
fork()

理解 fork: 习题 (2)

阅读程序,写出运行结果

for (int i = 0; i < 2; i++) {
    fork();
    printf("Hello\n");
}

状态机视角帮助我们严格理解程序行为

  • ./a.out
  • ./a.out | cat
    • 计算机系统里没有魔法
    • (无情执行指令的) 机器永远是对的
2024 南京大学《操作系统:设计与实现》