Linux TCP 调度与伸缩性

Linux TCP 大部分工作在 softirq 中,而 softirq 既可以无条件高优先级在 hardirq 后执行,也可在 percpu ksoftirqd 内核线程中执行,而后者饱受诟病,ksoftirqd 本质上就是普通 task,它可能被应用 task 挤压。

在 Linux kernel 程序员眼里,softirq 天生高贵,但 top/sar 又容不得 softirq 跑高,程序员的狭隘和偏见使然。

事情的另一面,softirq 也可能挤压应用进程,特别在数据中心高速传输场景,Linux softirq 的调度方式表现得尤其不可伸缩。softirq 在 hardirq 后最多可以执行 10 rounds 以及 2ms,取下限:

/*
 * We restart softirq processing for at most MAX_SOFTIRQ_RESTART times,
 * but break the loop if need_resched() is set or after 2 ms.
 * The MAX_SOFTIRQ_TIME provides a nice upper bound in most cases, but in
 * certain cases, such as stop_machine(), jiffies may cease to
 * increment and so we need the MAX_SOFTIRQ_RESTART limit as
 * well to make sure we eventually return from this method.
 *
 * These limits have been established via experimentation.
 * The two things to balance is latency against fairness -
 * we want to handle softirqs as soon as possible, but they
 * should not be able to lock up the box.
 */
#define MAX_SOFTIRQ_TIME  msecs_to_jiffies(2)
#define MAX_SOFTIRQ_RESTART 10

这时间对数据中心短肥管道及其漫长,足以饿死应用。对高速 TCP,如果应用程序没机会 write 将数据写入 queue,再高优的 softirq 触发 xmit 也难为无米之炊。

上周的讨论,我建议将一次 round 与连接数解耦,而每次处理一个连接的时间片做成连接数的减函数。比方说 10 万并发,softirq 最多 2ms,100 万并发降为 500us,这便提供了伸缩性。

说白了还是 Linux 调度太粗糙。上述应用被 softirq 挤占的问题,我使能软中断线程化就能解决,但应用程序会反过来挤占 softirq,众口难调,你能堆 patch,但解决不了问题。还是四象限法靠谱,但要动大手术。

理论上讲,TCP ACK 要比 send data 更高优,特别是携带 SACK 的 ACK,TCP 规范里明确 receiver 对非连续报文需关闭 delayed ack 而立即回应,这说明 TCP 希望 sender 更快响应异常事件,落实到实现就是高优处理 ACK,但 Linux kernel 不给力,它没能做到高优处理 ACK 同时不饿死应用。这也是用户态协议栈实现高速 TCP 的原因,自己实现调度。但另一方面,往大了说,想要高吞吐,何必用 TCP。

浙江温州皮鞋湿,下雨进水不会胖。

你可能感兴趣的:(tcp/ip,linux,网络)