Skip to content

Lec 16 高性能网络与调度

课件

本节讨论操作系统在支持高性能网络时面临的挑战。阅读论文:Shenango: achieving high CPU efficiency for latency-sensitive datacenter workloads(NSDI'19)

思考题(论文 Figure 2):Shenango 的整体设计和 xv6 网络实验有何不同?例如,在 xv6 里应用能直接把包写进网卡(NIC)队列吗?xv6 net 实验里有几个 NIC 队列?(见下文「与 xv6 网络实验的关系」一节。)

总览

  • 问题背景:数据中心延迟敏感负载、尾延迟(tail latency)、低延迟 vs 高利用率的矛盾
  • 传统 OS 网络为什么慢:中断、系统调用、上下文切换、每包 CPU 预算
  • 内核旁路(kernel bypass):DPDK、RSS 队列 steering、轮询、per-core 隔离、ZygOS 基线
  • Shenango 设计:IOKernel + 每应用 runtime + 不改的 Linux;5µs 拥塞检测;核分配算法
  • 与 xv6 网络实验的关系(思考题)
  • 论文重点图(Figure 2、拥塞检测、性能结果)

一、问题背景:数据中心的延迟敏感负载

典型负载:memcached 这类内存 KV 存储,单次 get/set 只要 ~1µs——应用本身不是瓶颈,系统开销才是

尾延迟(tail latency)为什么是关键?关注的不是平均延迟,而是 99 分位。Google 的经验:典型响应 10ms,但每 100 个请求就有 1 个慢到 1 秒。放大效应:一个用户请求可能扇出到 100+ 台机器,只要每台有 1% 概率慢 1 秒,大量终端用户就会感到卡顿。

延迟的来源:多应用争抢机器、后台守护进程、突发时包排队、核间负载不均。

核心矛盾:低延迟 vs 高利用率为压低尾延迟,运营商只能让数据中心跑在 20–30% 的低利用率,预留的核大量闲置、浪费资源。目标:既要低尾延迟、又要高利用率——靠动态共享核来达成。

二、为什么传统 OS 网络慢

Linux 收包路径很长:

NIC → DMA 进 rx ring → 中断 → TCP/UDP 处理 → 拷到 socket 队列
→ 唤醒应用 → 系统调用开销 → 应用 read → 发包 → tx ring → NIC 发出

瓶颈:

  • 中断开销:约束在 ~100 万次/秒;
  • 系统调用:上限几百万次/秒;
  • 上下文切换:睡眠/唤醒线程带来延迟;
  • 数据搬运:核间缓存一致性流量。
每包 CPU 预算算一笔账10 Gbps 以太网 ≈ 每秒 1000 万个 100 字节小包。一颗 2.4GHz 核每包只有 ~240 个周期,8 核也只有 ~1920 周期/包——而中断、系统调用、上下文切换轻易就吃光这点预算。所以「每包都进内核」根本来不及。

三、内核旁路(kernel bypass)

核心思路:把 NIC 队列和专属 CPU 核直接交给应用,绕过 Linux 内核,在用户态实现一个最小 OS(libOS)。

  • 用户态库设计:直接访问 NIC 的 rx/tx ring;在用户态实现完整 TCP/IP 栈;在专属核上做多线程调度。
  • RSS 队列 steering:网卡按 {srcIP, dstIP, srcPort, dstPort} 五元组做一致性哈希,把同一条连接固定 steer 到同一个队列/核——理想情况下均匀分布、负载均衡。
  • 轮询代替中断:专属核不停轮询 NIC ring,消除中断开销(前提是核被独占、不共享)。
  • per-core 隔离的好处:每个核管自己的空闲包链表和 TCP 结构 → 无锁争用、无核间通信、单线程事件循环,同步极简。
基线系统 ZygOS(纯内核旁路)低负载下中位延迟 ~35µs、尾延迟 300–400µs,全程压得住。缺点:所有核都被预留独占,低负载时核大量闲置、无法像 Linux 那样把核共享给批处理任务——利用率低。

四、Shenango 的设计

目标:保留内核旁路的低延迟,同时把闲置的核动态回收给别的应用;当网络应用需要更多核时,及时抢占批处理任务、避免「计算拥塞」。

三层架构

角色
IOKernel一个有 root 权限的专属 Linux 进程,独占一个核;坐在 NIC 与各应用 runtime 之间,每 5µs 扫描所有输入/输出队列,并把剩余的核动态分配给各 Shenango 应用;用软件把包 steer 到合适的核(比重编程网卡便宜)。
应用 runtime(每应用一个用户态 OS)用户态线程(green threads)+ 用户态 TCP/IP 栈 + per-core 包队列;保留最少核数,空闲核多时可「burst」借用。
Linux 内核不改动,继续跑后台任务;用 pthread affinity 把线程钉在核上;IOKernel 与 runtime 之间用共享内存 ring 通信。

拥塞检测(Algorithm 1,每 5µs 一次)

怎么判断「某应用核不够用了」?IOKernel 每 5µs 检查两类队列:① 某个包连续两次扫描都还在输入队列里 → 包在堆积;② 某个用户线程(uthread)一直留在 runtime 的 runqueue → 本地核处理不过来。任一成立就说明该应用核不够,需要加核。

核分配(Algorithm 2)——优先级

  1. 优先选「和该应用已有超线程同核」的核(缓存局部性最好);
  2. 其次选该应用最近刚让出的核;
  3. 再次随便挑一个空闲核;
  4. 实在没有,就抢占某个正在 burst 的(非预留)应用的核。

本质:尽量复用缓存、减小新核的冷启动延迟。Shenango 不是生产系统,而是「在 Linux 之上巧妙实现的一个新 OS」的研究成果——说明延迟敏感网络需要 OS 级的重新设计,而非仅靠库优化。


五、与 xv6 网络实验的关系(思考题解答)

Q:在 xv6 里应用能直接把包写进 NIC 队列吗?xv6 net 实验有几个 NIC 队列?
  • 应用不能直接碰 NIC。xv6 走的是传统内核中介模型:用户进程通过系统调用(socket/read/write)进内核,由内核里的 E1000 驱动e1000.ce1000_transmit / e1000_recv)去操作网卡的发送/接收环。应用碰不到 NIC 的 DMA 环——这正是 Figure 2 里「传统系统应用无法访问 NIC 硬件队列」的对照。
  • xv6 net 实验只有一对队列一个 TX 环 + 一个 RX 环(各 16 个描述符),单核处理、靠中断收包。
  • Shenango 则相反:每个核一个队列(实验里 16 个 NIC 队列),应用 runtime 在用户态直接轮询属于自己的共享内存包队列,IOKernel 用软件把包按核分发——是 kernel-bypass + 多队列 + 轮询的路子。
xv6 net 实验Shenango
应用能否直接访问 NIC 队列❌ 必须经内核 E1000 驱动✅ 用户态 runtime 直接轮询
NIC 队列数1 个 RX + 1 个 TX多个(每核一个,实验 16 个)
收包方式中断轮询(专属核)
协议栈内核里用户态 runtime

六、论文重点图

按整理惯例,论文部分只记录图中最重点的内容。

Figure 2:整体架构

  • Shenango 把数据面拆成特权的 IOKernel(独占核)和非特权的每应用 runtime
  • 包从 NIC 进来 → IOKernel 解复用 → 分发到共享内存队列 → 应用 runtime 直接轮询取走,全程绕过传统内核网络栈。
  • 与常规系统形成鲜明对比:常规系统里应用根本访问不到 NIC 硬件队列

拥塞检测机制图/算法

  • IOKernel 约每 5µs 检查一次系统状态,同时盯包队列(NIC 积压)和应用 runqueue(待处理工作)。
  • 队列深度上升 = CPU 不够 → 把核从批处理任务收回给网络应用。这个基于轮询的反馈环实现了亚毫秒级的负载响应。

核 steering / RSS

  • IOKernel 用 RSS 把流量导向特定核——不是均匀摊开,而是按流 steer 到应用核,减少缓存失效与协调开销。

性能结果(Pareto 前沿与利用率)

  • 延迟:请求-响应负载下尾延迟低至个位数微秒(Linux/IX 是毫秒级)。
  • CPU 效率:网络低负载时能腾出 10–15 个核给批处理计算。
  • 延迟-吞吐折中:比 Arachne、IX、ZygOS 好约 10× 的 Pareto 前沿;在 memcached、RocksDB 等真实负载上全面优于 Linux/IX/ZygOS。
  • 注意 Shenango 的延迟曲线比 ZygOS 更早开始上升——因为它在动态共享核:负载升高、预留核占满后需要抢占批处理、重分配核,这一步引入了额外延迟(但换来了高利用率)。

自测清单

  • [ ] 为什么数据中心关注 99 分位尾延迟而非平均?扇出如何放大尾延迟?
  • [ ] 传统 OS 网络慢在哪几处?「每包 CPU 预算」为什么撑不住 10Gbps 小包?
  • [ ] 内核旁路靠哪几招提速(队列直通、RSS、轮询、per-core 隔离)?ZygOS 的优缺点?
  • [ ] Shenango 三层各管什么?IOKernel 每 5µs 在检测什么、据此做什么?
  • [ ] 低延迟 vs 高利用率的矛盾,Shenango 如何同时拿下?
  • [ ] xv6 net 实验里应用能直接写 NIC 队列吗?有几个队列?和 Shenango 差在哪?