A fork() in the road

本文最后更新于:2024年8月11日

本文是A fork() in the road的读书笔记

作者认为,fork 作为操作系统原语阻碍了系统研究;作为教育工作者,应该把 fork 当作历史的产物来教,而不是学生遇到的第一个进程创建机制。

fork 有一些优点:

  • 简洁
  • 避免并发,当然是在有线程和异步之前。

作为一个存在了50年的抽象,在当今fork也有很多缺点:

  • fork 已经不再简洁了,如果只有内存和文件描述符,这是十分优雅的方案,但是操作系统的演化过程中,进程增加了更多的东西。如今 POSIX 标准列出了 fork 的25种特殊情况,包括文件锁,tracing 等等。

  • Fork doesn’t compose。不适用于用户空间的很多抽象,比如说 Buffered IO

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

    在笔者的电脑上(ubuntu 22.04)直接运行该程序会输出6行Hello,但是管道给wc -l后结果是8 具体可以看jyy在B站的视频讲解

  • 线程安全问题。比如某个线程使用 malloc 获取到了堆区的锁,这个时候 fork,子进程一旦调用 malloc ,就会死锁。

  • 安全性,子进程会继承父进程的每一个字节

  • 性能低, copy-on-write 能够一定程度上解决这个问题,但是如今创建 copy-on-write 的映射也会造成一定程度的性能开销。

  • 可扩展性,fork需要复制状态机的每个方面,这意味着实现fork需要统一管理系统状态,这在宏内核里很容易实现,但是微内核中这很难。

  • Fork encourages memory overcommit。作者认为,fork 时并不会请求所有的内存,而是 copy-on-write 复用父进程的内存,需要修改时再申请,这样会造成意料之外的 oom

  • fork 并不适用于单地址空间,并且 fork 影响了很多并不基于 unix 系统的科研系统,他们要考虑是否实现fork 语义,若实现,则很可能影响其原有的设计,束缚原先的设计。

  • fork 不适用于异构设备,毕竟没办法复制异构设备的状态。

  • fork 会感染一整个系统,需要每一个层次都支持 fork

作者认为,shell 很适合使用 fork,但是大多数的现代应用并不是 fork

作者最后提出了一些愿景:

  • 弃用 fork
  • 改进比如 posix_spawn() 这样的替代品
  • 修改教学,就像没有人会在一开始教授goto,我们也不应该讲授如何使用 fork 创建进程