系统与基础设施jvns.ca
为什么我们使用 Linux 内核的 TCP 栈?
Why do we use the Linux kernel's TCP stack?
2016 — 2020,它一次次回到首页
黑点是上首页(≥ 50 分)的那次投稿,空心点是没冲上去的投稿,点越大分数越高。
导读
Julia Evans 写的一篇科普/辨析文:既然有人主张把网络栈搬到用户态以追求性能,那为什么大多数程序还是直接用 Linux 内核自带的 TCP 栈?文章梳理了内核栈在功能完整度、成熟度、可维护性上的优势,以及什么情况下用户态栈才值得考虑。
为什么经久不衰:它把"要不要自己重写网络栈"这个反复出现的工程诱惑讲清楚了,评论区里多位亲历者的血泪经验比文章本身更耐读,对今天做高性能网络的人仍是必修。
HN 评论精选
重写网络栈是典型的过早优化,除非你是 Google/Facebook 这种规模。
除非你能养一个全职团队来支持它,否则千万别重写自己的网络栈。我职业生涯中两次参与重写 IP/TCP 栈的团队,理由各不相同,但都不是性能。项目初期信心满满、进展神速:"快多了""我的代码比内核版本简洁多了,我真聪明!"我们发布了能用的版本,热情高涨。然后 bug 报告开始涌来:原本干净的实现被我们当初没领会到的规范细节塞满;我们花了几周去追别的网络栈的怪异行为——那是互联网"事实规范"里没写进文档的部分。为了兼容它们,代码越来越乱,性能也下降了。两次都是一年后,我们后悔得不行,开始计划摆脱这个依赖,而此时迁移已有部署变得更麻烦。我不会再犯第三次。
jjguy查看英文原文
Please don't rewrite your network stack unless you can afford to dedicate a team to support it full time. Twice in my career I have been on teams where we decided to rewrite IP or TCP stacks. The justifications were different each time, though never perf. The projects were filled with lots of early confidence and successes. "So much faster" and "wow, my code is a lot simpler than the kernel equivalent, I am smart!" We shipped versions that worked, with high confidence and enthusiasm. It was fun. We were smart. We could rewrite core Internet protocol implementations and be better! Then the bug reports started to roll in. Our clean implementations started to get cluttered with nuances in the spec we didn't appreciate. We wasted weeks chasing implementation bugs in other network stack that were defacto but undocumented parts of the internet's "real" spec. Accommodating these cluttered that pretty code further. Performance decreased. In both cases, after about a year, we found ourselves wishing we had not rewritten the network stack. We started making plans to eliminate the dependency, now much more complicated because we had to transition active deployments away. I have not made that mistake a 3d time. If you are Google, Facebook or another internet behemoth that is optimizing for efficiently at scale and can afford to dedicate a team to the problem, do it. But if you are a startup trying to get a product off the ground, this is Premature optimization. Stay far, far away.
成熟协议栈里一处看似无害的改动就能引入难以定位的 bug。
多年前我在一家做网络硬件的公司,买了一套实现各种路由协议(含 TCP/IP 层)的代码,其实就是 BSD 栈。我们的开发板启动时会用 FTP 下载最新镜像。切换过去后偶尔下载失败——设备会发出莫名其妙的 RST 掐断连接。作为协议组的人,这活落到我头上。我盯着 tcpdump 看了三天,加了一堆调试输出,最后把 Stevens 的书摊在腿上逐行读代码,终于找到问题:他们对 BSD 代码只改了一处,就引入了这个依赖编译器的 bug。写对这套代码真的很难。
tingley查看英文原文
Years ago I worked for a company who was building a piece of networking hardware, and we had bought some code that implemented various well-known routing protocols. This included the TCP/IP layer, but it was just the BSD stack. Our dev hardware had a boot loader that would use FTP to download the most recent images to finish the boot. After we cut over to this code, we started having an occasional problem where this download would fail -- the box would send a spurious RST and kill the connection. I was one of the protocols people, and it ended up on my plate. I spent 3 days staring at tcpdump, adding enormous piles of debug traces, and finally going through the code line-by-line with a copy of the Stevens book open on my lap. Eventually I found the problem - they had made one single change to the BSD code, and in so doing introduced the bug. It was compiler-dependent. Writing this code correctly is hard.
内核栈的每个复杂部件都在解决真实问题,先理解再谈替换。
Linux 网络栈一直在快速演进,现在有了 XDP 这样的内核快速路径。但里面还有很多部件,用来在嘈杂网络上保证高吞吐、适配不同应用负载模式、避免 bufferbloat,并在多进程/多容器之间公平分配。我猜一个人可以做出在实验室 iperf 微基准里看起来更好的用户态栈,但在真实应用和真实网络里会输(除非你的场景非常窄)。我建议先理解这个栈再决定替换它——就像老程序员说的,拆掉一扇门之前先搞懂它是干什么的。我在下一本书 sysperf2 里总结了 TCP 发送路径,光发送路径就涉及:发送缓冲、pacing、TSO、拥塞控制、Nagle、TSQ、qdisc、GSQ、BQL、网卡 TSO。这些缩写如果你都没见过,那正是我的意思:它们各有用途,从零开始你迟早会把它们全部重新发明一遍。
brendangregg查看英文原文
The Linux network stack has been rapidly evolving; nowadays we have XDP for a fast path through the kernel. But there's also a lot of moving parts that ensure high throughput on noisy networks, for different application workload patterns, to avoid bufferbloat, and to do this fairly across multiple processes/containers. I'd imagine one can develop a user-space stack that appeared better in a simple iperf microbenchmark in a lab, but would lose for real-world applications and networks (unless your real-world use case was very narrow). I'd recommend understanding the stack before choosing to replace it (the old programmer's advice about understanding a gate before removing it). I summarized the TCP send path in my next book (sysperf2, with help from Linux network engineers); it involves: - Socket send buffers - Pacing - TSO - Congestion controls - Nagle - TSQ - qdisc (optional) - GSQ - BQL - TSO (NIC) That's just the TCP send path. I'm happy to expand all these acronyms, but if any are new to you, that's kind-of my point. These parts have a purpose, and starting from scratch may lead you to eventually re-invent them all as you encounter the problems they solve.
单机规模今非昔比,内核统一参数未必适配所有进程,Snap 是一类折中方案。
当进程几乎和整台机器一样大时,用内核 TCP 栈很合理:一台机器一个 CPU、32MB 内存跑个 Web 服务器,为什么不用内核栈?但现在一台主机抵得上过去整个网络,单机 256 核、同时跑几百个进程。内核里写死的常量和那点系统级调参不可能同时服务好所有进程。可问题是,机器仍只有一两个网卡,总得有东西管理访问。Snap(微内核式的主机网络架构)就是一个有意思的解法:绕开内核网络、统一中介网卡访问,并给每个应用定制协议栈和参数。
jeffbee查看英文原文
The kernel TCP stack made a lot of sense when processes were almost as large as machines. When you had 1 CPU and 32MB of memory to run a web server then why wouldn't you use the kernel stack? But these days the host is as large as what we used to consider entire networks. You can easily have 256 CPU cores in a box now, and that box will be running hundreds of different processes at a time. Not all of those processes will be served equally well by whatever constants are defined in the kernel, and by whatever system-wide tuning parameters are available. And yet, there's still the problem that the box has one or a few hardware network interfaces, and something has to manage access to those resources. The architecture described at [1] is an interesting solution to this problem which avoids kernel networking, mediates NIC access, and provides custom stacks and parameters to each application. 1: Snap: a microkernel approach to host networking https://blog.acolyer.org/2019/11/11/snap-networking/
历次讨论
同类还值得读
TTY 揭秘(2008)
The TTY demystified (2008)
经典文章深入剖析了 Unix/Linux 中 TTY 子系统的工作原理,包括线路规程、终端驱动、信号与控制字符等机制。
系统与基础设施上首页 11 次 · 2009–2025linusakesson.net编辑精选为什么 GNU grep 这么快
Why GNU grep is Fast
FreeBSD 邮件列表上一篇广为流传的帖子,解释 GNU grep 为什么快:核心在于 Boyer-Moore 字符串搜索算法配合精心优化的内部循环和 I/O,让程序在搜索时“…
系统与基础设施上首页 9 次 · 2010–2022lists.freebsd.orgXplain:为普通人讲解 X11
Xplain – Explaining X11 for the rest of us
《Xplain》是作者(HN 用户 Jasper_)写的一系列 X11 长文,用他在浏览器中重建的 X11 绘图模型做交互演示,讲解 X11 的绘图模型与窗口系统设计。
系统与基础设施上首页 6 次 · 2013–2020magcius.github.io编辑精选旋风教程:为 Linux 创建极小 ELF 可执行文件(1999)
A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux (1999)
breadbox 写于 1999 年的经典教程,主题是如何为 Linux 手工构造并裁剪出体积极小的 ELF 可执行文件。
系统与基础设施上首页 6 次 · 2007–2025muppetlabs.comPostgreSQL 内部原理
The internals of PostgreSQL
《The Internals of PostgreSQL》是一本免费在线书籍,系统讲解 PostgreSQL 的内部实现,包括查询处理、并发控制、WAL、缓冲区管理等核心机制。
系统与基础设施上首页 5 次 · 2017–2025interdb.jp编辑精选