Linux 7.2 的合并窗口到 6 月中旬基本收尾。在大量 Btrfs、XFS、x86 清理和硬件支持改动之外,有三条 VFS 相关补丁跨度最大,却指向同一个模式。它们分别优化 shell 管道的写入性能、消除容器退出时的卸载延迟、给 openat2 增加了一个只允许打开普通文件的标志。三条补丁的作者分别是 Meta 的 Breno Leitao、阿里巴巴的李宝坤,以及个人贡献者 Dorjoy Chowdhury(由 VFS 维护者 Christian Brauner 合入)。不同背景,不同场景,相同做法:在已经工作多年的代码路径里找到隐藏瓶颈,移除。
管道写入:在锁外预分配 8 页,避免内存回收拖慢读端
Meta 工程师 Breno Leitao 在分析公司内部缓存代码时发现,anon_pipe_write 在持有 pipe->mutex 期间逐页调用 alloc_page。内存分配可能因 direct reclaim 进入睡眠,还可能触发 memcg 记账——锁一直被持有,任何试图读取同一管道的进程都会被阻塞。
补丁的逻辑是:在获取 mutex 之前预分配最多 8 个页面,持锁后消费这些预分配页,剩余的写完后释放。分配器不再出现在管道的临界区内。
性能数据来自一个 writers × readers 扫描测试,64KB 写入对 1MB 管道:正常负载下吞吐量提升 6%–28%,写延迟下降 5%–22%。真正有意义的是内存压力下的数据——当 direct reclaim 最容易被触发时,吞吐量提升 21%–48%,延迟下降 17%–33%。这不是"管道变快了",而是"管道在系统紧张时不会再拖累读端"。
shell 中每个 | 都对应一个匿名管道。大量脚本、CI 流水线、日志处理在高并发中使用 shell 管道,6%–28% 的吞吐改善对于 cat huge.log | grep pattern | sort | uniq -c 这种链式管道意味着累加。内存压力下的数字则关系到容器化环境中的稳定 tail-latency。
容器卸载:92 毫秒到 5 毫秒,去掉了全局序列化
阿里巴巴工程师李宝坤追踪了一个竞争条件:容器退出时,cgroup_writeback_umount() 和 inode_switch_wbs() 之间存在一个窗口。cgroup_writeback_umount 检测到全局 isw_nr_in_flight 计数器非零,但 flush_workqueue 还没排入任何任务,函数提前返回——留下一个未释放的 inode 引用,阻塞 evict_inodes(),后续的 iput() 访问已释放的 percpu 计数器,触发 use-after-free 和 "VFS: Busy inodes after unmount" 消息。
Bug 修复之后,紧接着是性能改造。原代码使用全局 synchronize_rcu() / flush_workqueue() 对,意味着任何一次卸载都会等待所有 superblock 上的 writeback 切换完成,而不是只等待自己的。补丁替换为 per-superblock 的 in-flight 计数器加 pin/unpin/drain 辅助函数,卸载不再与不相关的 superblock 串行化。
在 16 vCPU 的虚拟机中运行 cgroup writeback 压力负载:卸载延迟的 p50 从 92–138ms 降至 5–8ms。cgroup_writeback_umount() 的单次累积成本从约 62ms 降至约 4μs。
每秒钟启动销毁成百个容器的编排系统中,92ms 到 5ms 是退出延迟上的一个数量级改善。阿里的容器密度决定了这个数字的意义。
OPENAT2_REGULAR:浏览器不该打开 /dev/random
第三条改动看似最小:openat2 新增一个 OPENAT2_REGULAR 标志。当进程设置此标志后,任何对非普通文件(设备节点、FIFO、socket、目录)的 open 操作都返回 EFTYPE。同一个 pull 还增加了 O_EMPTYPATH,允许从 O_PATH 文件描述符获取可操作的文件描述符。
Phoronix 的配图是一个浏览器试图打开 /dev/random 的截图——这从来不该发生。更实际的场景是:一个被入侵的进程试图通过打开 /dev/mem 或操作设备节点来提权。OPENAT2_REGULAR 提供了一条进程级别的限制:如果程序被声明为只需要普通文件,内核在系统调用层就拒绝了其他一切。
这项改动不是安全特性本身,而是让进程的预期行为变得可执行验证。sandbox 项目(如 Chrome 的 seccomp-bpf 过滤器、Flatpak、systemd 的服务沙盒)可以在启动时设置此标志,把"这个进程不需要设备文件"从注释变成可强制执行的系统调用约束。
三个补丁的共性:不添加新路径,移除旧路径里的瓶颈
三条补丁覆盖了三个完全不同的场景——进程间通信、容器基础设施、进程沙箱——也来自三种完全不同的背景:大厂、云厂商、个人贡献者。共同点是:没有新增架构概念,没有引入新的抽象层,而是找到已有路径里一个很早就存在但在放大后才暴露的瓶颈。管道逐页分配在内核写了二十年,卸载全局序列化是 cgroup writeback 从第一天就有的设计,而程序对设备文件的防御直到 openat2 有了新 flag 之后才变成声明式约束。
把它们放在一起读,Linux 7.2 在内核子系统里发生的不是"一轮大改",而是数条积累多年的技术债务在各自不同的触发条件下同时被清理。