Li Chen 给 Linux 内核提了一个 spawn templates 补丁。
目标很窄:有些工具会反复启动同一个可执行文件,比如频繁调用 Git 的构建工具、开发工具链。既然目标程序一样,能不能把一部分启动信息缓存下来,下次少走几步?
补丁给出的基准提升约 2%。不大,也不能吹成性能革命。
但这事真正有意思的地方,不是 2%。内核讨论很快转向另一个问题:Linux 还要继续围着 fork()+exec() 打补丁,还是该给“创建一个新程序”一条更干净的路?
spawn templates 戳中了痛点,但不是答案
spawn templates 的思路不复杂。
应用先创建一个模板。内核打开目标可执行文件,并缓存一些后续 exec 会用到的信息。真正启动时,再传 argv、envp、文件描述符动作、信号处理等差异化配置。
同一个程序反复上场,别每次都从后台重新登记一次。
| 问题 | 传统路径 | spawn templates 想做什么 | 现实限制 |
|---|---|---|---|
| 典型动作 | fork()+exec() | 缓存同一可执行文件的启动信息 | 只适合重复启动同一程序 |
| 受益场景 | 构建工具、开发工具链频繁拉起子进程 | 分摊启动准备成本 | 普通应用未必有感 |
| 示例 | 反复调用 Git | 少做部分重复工作 | 不是通用加速器 |
| 基准结果 | 正常进程启动路径 | 约 2% 提升 | 不能夸大 |
| 合入前景 | 仍是主流路径 | 当前方案基本不会进内核 | 讨论已转向新 API |
这里最容易误读的一点是:fork() 不是把父进程的物理内存完整复制一遍。
Linux 有写时复制。很多成本已经被摊薄。
但 fork() 仍然要复制父进程状态、页表和大量上下文。随后 exec() 又把很多刚准备好的东西丢掉。这个路径优雅,但不免费。
Unix 当年这套设计很漂亮:fork() 复制自己,exec() 换上新程序。两个积木拼出复杂行为。
可现代软件把“启动进程”用得太频繁。构建系统、语言运行时、shell、服务管理器、容器工具,都可能把子进程启动当日常消耗。老积木开始硌手。
对工程团队来说,这意味着一件很实际的事:现在不用为了 spawn templates 改代码。它不是一个马上可用的内核能力。
但如果你维护的是构建工具、运行时或大量调用外部命令的系统,应该开始盯住 pidfd 和 posix_spawn() 的后续接口设计。真正可能改变代码路径的,不是这个模板补丁,而是新的进程创建原语。
分歧不在缓存,而在要不要复制父进程
Mateusz Guzik 的批评很直接:fork()+exec() 这个惯用法本身就很糟糕,应该退休。
他的判断点很清楚。spawn templates 只是给 exec 前后的部分流程做缓存,但没有动 fork() 这块主要成本。与其先复制当前进程,再丢掉大部分状态,不如直接创建一个 pristine process,一个干净的新进程。
这才是争论的核心。
| 路线 | 基本思路 | 好处 | 代价 |
|---|---|---|---|
| 修补 fork()+exec() | 继续沿用旧模型,在局部做优化 | 兼容性强,生态扰动小 | 主要低效来源仍在 |
| spawn templates | 缓存同一可执行文件的部分启动信息 | 对重复启动同一程序有帮助 | 场景窄,收益有限,API 方向被质疑 |
| pidfd 新进程 API | 先创建空进程,再逐步配置 | 更接近干净的进程创建模型 | API 设计复杂,合入门槛高 |
| 原生 posix_spawn() | 不再暗藏 fork()+exec() | 用户态可获得更直接路径 | 需要内核提供合适原语 |
Christian Brauner 的方向更像内核治理。
他并不反对给 exec 做 builder API,但更倾向把新接口建在 pidfd 上:先通过类似 pidfd_open 的机制创建空进程,再用新的 pidfd_config() 去配置它,包括环境、可执行映像等。这有点像 fsconfig() 管文件系统配置的思路。
重点是支撑用户态真正实现 posix_spawn()。
posix_spawn() 不是新标准,也不是新概念。它本来就适合表达“启动一个新程序”。问题在于,Linux 上很多实现路径仍然会把 fork()/exec() 藏在下面。
开发者想要的不是换个函数名继续走老路,而是一条原生路径。
这会影响谁?不是普通桌面用户先感知。
更相关的是两类人。
一类是运行时、shell、构建工具和语言生态的维护者。他们要判断未来是否能把高频子进程启动切到更直接的 posix_spawn() 路径,而不是继续靠 fork()+exec()。
另一类是容器、服务管理器和系统工具开发者。他们更在意进程能不能被稳定引用、配置和管理。pidfd 路线如果继续推进,进程创建会更像“配置一个对象”,而不是“复制当前进程再改掉它”。
眼下的动作不该是迁移,而是观察接口形态:pidfd_config() 是否成形,能配置哪些状态,是否足以覆盖现有 posix_spawn() 需求,以及 glibc、musl 这类用户态实现会不会跟进。
我更在意 Linux 这次开始认账
spawn templates 本身不是大新闻。
2% 的收益,窄场景,方案还被质疑。它不像一个马上改写 Linux 性能版图的补丁。
但它逼出了一个更诚实的问题:fork()+exec() 的美学红利,还能不能覆盖现代进程启动的成本?
“天下熙熙,皆为利来。”技术世界也一样。
API 能活几十年,不只是因为优雅,也因为迁移成本巨大、生态惯性巨大、没人愿意为破坏兼容背锅。fork()+exec() 是 Unix 传统的一部分,也是无数程序默认假设的一部分。动它,不靠一句“更现代”就能赢。
所以 pidfd 路线看起来更现实。
它不急着废掉 fork()+exec(),而是在旁边长出一条新路:进程先是一个可引用、可配置、可治理的对象,然后再被启动。
这和 Linux 近些年的方向是顺的。pidfd 本来就在补传统 PID 模型的坑。现在如果连进程创建也纳入这套抽象,逻辑并不突兀。
我的判断是:这次少见地问对了问题。
问题不在 spawn templates 这个补丁够不够漂亮,而在 Linux 是否愿意给“启动一个新程序”提供不依赖父进程复制的正经原语。
这也有现实约束。
新 API 不能只为某个基准好看。它必须能覆盖文件描述符动作、信号、环境变量、凭据、命名空间等现实需求。还要让用户态库愿意采用。否则它只是多一个系统调用,多一层维护负担。
fork()+exec() 不会消失,也不该轻易消失。旧制度能活到今天,必有其强处。
但强处不等于永远正确。
这次讨论真正露出来的,是 Unix 传统、性能现实和 Linux API 治理之间的一笔旧账。spawn templates 可能过不了关,账还在。
