超越 fork() + exec()

Lobsters Hottest 新闻

摘要

一项向Linux内核添加spawn模板的提案,旨在通过缓存可执行文件信息来优化fork+exec模式,不过当前的补丁集不太可能被原样接受。

<p><a href="https://lobste.rs/s/8tr6xi/moving_beyond_fork_exec">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/06/08 03:16

# 超越 fork() + exec() 来源:https://lwn.net/SubscriberLink/1076018/16f01bbbb8e0d1f0/ ## \[LWN 订阅者专属内容\] > ### 欢迎访问 LWN.net 以下订阅专属内容由一位 LWN 订阅者为您提供。成千上万的订阅者依赖 LWN 获取来自 Linux 和自由软件社区的最佳新闻。如果您喜欢这篇文章,请考虑订阅 LWN(https://lwn.net/subscribe/)。感谢您访问 LWN.net! 自 Unix 诞生早期以来,两个核心的进程导向系统调用一直是 `fork()`(创建一个子进程作为父进程的副本)和 `exec()`(用新程序替换当前程序)。在 Linux 内核中,这些系统调用更常被称为 `clone()`(https://man7.org/linux/man-pages/man2/clone.2.html)和 `execve()`(https://man7.org/linux/man-pages/man2/execve.2.html),但核心功能保持不变。虽然这种进程创建模型有其优雅之处,但也存在缺陷。Li Chen 最近提出的一项在内核中添加“spawn 模板”的提案(https://lwn.net/ml/all/[email protected]),目前的形式不会被接受,但它可能指向未来一种新的进程创建原语。 `fork()` 是一个相对昂贵的系统调用;它必须为子进程复制整个进程状态(包括内存)。多年来已经进行了许多优化,但 `fork()` 本质上仍然是一项成本高昂的操作。更糟糕的是,`fork()` 调用之后通常紧跟着一个 `exec()`,而 `exec()` 会丢弃所有为子进程精心复制的内存。多年来,人们曾尝试(例如 `vfork()`(https://man7.org/linux/man-pages/man2/vfork.2.html))针对这种情况进行优化,但这种模式仍然比其应有的成本更高。 #### Spawn 模板 Chen 的补丁集采用了一种有趣的方法来优化 `fork()` 和 `exec()` 模式。它专注于那些重复启动运行同一个可执行文件的进程的应用程序;想象一下,一个程序需要反复运行 Git 来获取仓库内容的信息。在这种情况下,该程序可以建立一个模板来加速这些调用,将设置成本分摊到多次操作中。这个模板将通过 `spawn_template_create()` 系统调用创建: ``` struct spawn_template_create_args { __aligned_u64 flags; __s32 execfd; __u32 exec_flags; __aligned_u64 filename; /* 部分字段已省略 */ }; int spawn_template_create(struct spawn_template_create_args *args, size_t args_size); ``` 此调用将返回一个文件描述符,代表可执行文件的模板。该可执行文件可以指定为文件描述符(`execfd`)或绝对路径(`filename`),但不能同时指定两者。为了创建模板,内核将打开指定的文件并缓存一系列信息,使得将来能更快地运行该文件。 相关应用程序可能多次运行同一个可执行文件,但每次调用在多个方面有所不同。特定调用的详细信息必须放入该结构体的一个实例中: ``` struct spawn_template_spawn_args { __aligned_u64 flags; __aligned_u64 pidfd; __aligned_u64 argv; __aligned_u64 envp; __aligned_u64 actions; __aligned_u64 actions_len; __aligned_u64 reserved[4]; }; ``` `argv` 字段是指向传递给程序的参数列表的指针,`envp` 指向环境变量。而文件描述符和信号处理的更改则通过 `actions` 传递,`actions` 是一个指向以下结构体数组的指针: ``` struct spawn_template_action { __u32 type; __u32 flags; __s32 fd; __s32 newfd; __aligned_u64 arg; }; ``` 例如,如果应该关闭子进程中的文件描述符四,则相关的 `spawn_template_action` 结构体应将 `type` 设置为 `SPAWN_TEMPLATE_ACTION_CLOSE`,并将 `fd` 设置为四。还有其他用于复制文件描述符、打开文件、更改工作目录和更改信号处理的 action。 一旦填充好 `spawn_template_spawn_args` 结构体,就可以通过以下方式运行新进程: ``` int spawn_template_spawn(int template_fd, struct spawn_template_spawn_args *args, int args_size); ``` 在内部,该系统调用遵循接近正常的 `fork()`/`exec()` 路径。Chen 谨慎地指出,所有执行新文件时应用的常规检查仍然有效。但模板中缓存的信息使得整个过程比以前更快。快多少?在 cover letter 中提供的基准测试结果显示性能提升了约 2%,这看起来可能不多,但对于符合预期模式的应用程序来说,可能会产生显著的影响。 #### 迈向 `posix_spawn()` Mateusz Guzik 对此工作进行了最详细的审查(https://lwn.net/ml/all/vealb52tv5suireenkke4lul2l3wbnaul2rp3ea545ly5wa5ty@yk3aksvp7skt),他表示:“这个问题对我而言非常重要,我断断续续思考了很长时间。整个 fork + exec 的惯用方式很糟糕,需要被淘汰。”他指出,这个补丁集的关注点有点奇怪,因为它没有触及问题中的 `fork()` 部分。他说,大部分成本都在那里,因此优化工作应该致力于将其从场景中移除。与其复制当前进程,“创建一个全新的进程才是正确的方向”。 Christian Brauner 对该目标表示赞同(https://lwn.net/ml/all/20260528-madig-fachrichtung-fehlinformation-61117ba640da@brauner),他说:“为 exec 提供一个构建器 API 的想法并非完全疯狂。”不过,他的建议是,新的 API 应该建立在现有的 `pidfd`(https://lwn.net/Articles/794707/)抽象之上。在不深入细节的情况下,他表示正确的方法是为 `pidfd_open()`(https://man7.org/linux/man-pages/man2/pidfd_open.2.html)创建一个选项,用于创建一个空进程。然后,通过一系列对新系统调用 `pidfd_config()` 的调用,按需配置这个新进程,设置其环境、要执行的映像等等。这样,`pidfd_config()` 将类似于 `fsconfig()`(https://man7.org/linux/man-pages/man2/fsconfig.2.html)。 Brauner 表示,新接口的一个重要目标是能够在用户空间中支持 `posix_spawn()`(https://man7.org/linux/man-pages/man3/posix_spawn.3.html)的实现。`posix_spawn()` 非常适合替代 `fork()`/`exec()` 模式;开发者可能会欢迎一个本地的实现,它(与当前实现不同)不会在底层隐藏 `fork()` 和 `exec()`。Chen 同意(https://lwn.net/ml/all/[email protected])Brauner 大致勾勒的 API 看起来更好,并表示未来的工作将朝着这个方向进行。因此,Linux 内核中不会出现 spawn 模板,但如果 Chen 的后续工作取得成功,Linux 最终可能会获得一个合适的 `posix_spawn()` 实现。 本文的索引条目 内核(https://lwn.net/Kernel/Index) 系统调用/clone()(https://lwn.net/Kernel/Index#System_calls-clone) 内核(https://lwn.net/Kernel/Index) 系统调用/execve()(https://lwn.net/Kernel/Index#System_calls-execve)

相似文章

交换表、闪存友好的交换、swap_ops 等

Hacker News Top

本文介绍了 Linux 内核交换子系统的最新改进和未来计划,包括减少每页开销、基于 folio 的辅助函数,以及使交换更适配固态存储的努力。相关内容在 2026 年 Linux 存储、文件系统、内存管理与 BPF 峰会上进行了讨论。

内联函数的调试信息

Hacker News Top

Alan Maguire 在 Linux 存储、文件系统、内存管理和 BPF 峰会上主持了一场会议,提议对 BTF 进行扩展,以存储有关内联函数的信息,从而通过 kprobes 实现对这类函数的内核跟踪。

double-fork 是什么鬼?

Lobsters Hottest

一篇开发日志,解释了用于创建健壮的 POSIX 守护进程的 double-fork 技术,涵盖了在 Zig 中构建 zmx 时的 fork、会话和控制终端。