4. RPS 技术原理:Receive Packet Steering 工作机制

Receive Packet Steering (RPS) 是 Linux 内核在软件层面实现的一种中断负载均衡机制。它不修改硬件中断的分配方式,而是在中断处理完成后、数据包进入网络协议栈之前,通过软件方式将数据包的处理负载重新分发到多个 CPU 核心上。RPS 的核心思想是:让一个 CPU 处理硬件中断,让多个 CPU 共同处理软中断(SoftIRQ),从而避免单个 CPU 成为瓶颈。

核心目标: 解决硬件中断(MSI-X)队列数量有限,无法充分利用多核 CPU 的问题。RPS 在软件层实现“伪多队列”效果,提升整体吞吐量。

4.1 软件中断分发机制

RPS 的工作流程分为三个阶段:硬件中断处理 → 数据包入队 → 软件中断分发。其核心在于将数据包的处理(而非中断接收)分散到不同的 CPU 上。

  1. 硬件中断阶段: NIC 触发硬件中断,被分配到某个 CPU(通常由硬件或 irqbalance 决定)。该 CPU 执行中断处理函数(如 ixgbe_msix_clean),从 NIC 的 RX 队列中取出数据包。
  2. 数据包入队阶段: 中断处理函数调用 napi_gro_receive 或 netif_receive_skb,将数据包送入目标 CPU 的 backlog 队列(即 softnet_data 结构体中的 input_pkt_queue)。
  3. 软件中断分发阶段: 触发 NET_RX_SOFTIRQ 软中断。RPS 根据哈希值将数据包重定向到其他 CPU 的 backlog 队列,由目标 CPU 的软中断处理函数执行后续协议栈处理。
关键区别: 传统模式下,硬件中断和软中断都在同一个 CPU 上执行。RPS 模式下,硬件中断在 CPU-A 执行,而数据包处理(软中断)可能被分发到 CPU-B、CPU-C 等,实现真正的负载均衡。
RPS 软件中断分发流程 NIC 硬件中断 CPU-0 执行 ISR skb 入队 backlog CPU-1 软中断处理 CPU-2 软中断处理 CPU-3 软中断处理 说明: • 硬件中断阶段:NIC 触发中断,CPU-0 执行中断服务程序(ISR),从 RX 队列读取数据包。 • 入队阶段:数据包(skb)被放入 CPU-0 的 backlog 队列,等待 RPS 分发。 • 分发阶段:RPS 根据哈希值将 skb 重定向到 CPU-1/2/3 的 backlog 队列,由目标 CPU 的软中断处理。

4.2 哈希计算:数据包分发决策的核心

RPS 使用 哈希计算 来决定每个数据包应该被分发到哪个 CPU 的 backlog 队列。哈希计算的输入是数据包头部中的关键字段,输出是一个哈希值,该值对 CPU 数量取模后得到目标 CPU 编号。

4.2.1 哈希输入字段

RPS 支持多种哈希输入组合,通过 rps_hash_key 和 rps_hash_mask 控制。默认情况下,RPS 使用四元组(源 IP、目的 IP、源端口、目的端口)进行哈希计算。对于不同协议,哈希输入字段有所不同:

协议类型 哈希输入字段 说明
TCP / UDP 源 IP + 目的 IP + 源端口 + 目的端口 四元组哈希,保证同一流的数据包分发到同一 CPU
其他 L4 协议 源 IP + 目的 IP 仅使用 IP 地址,端口不可用时降级
IPv6 扩展头 流标签(Flow Label)+ 源 IP + 目的 IP 优先使用流标签,提高哈希均匀性
GRE / IPIP 隧道 内层 IP 头字段 支持隧道协议的内层哈希

4.2.2 哈希计算过程

RPS 的哈希计算在内核函数 skb_get_hash 中实现,使用 Toeplitz 哈希算法(与硬件 RSS 相同的算法)。计算过程如下:

  1. 提取字段: 从 skb 中提取协议相关的字段(如 IP 地址、端口)。
  2. 构造输入: 将提取的字段按固定顺序拼接成一个字节数组。
  3. 计算哈希: 使用 Toeplitz 哈希函数,配合一个 40 字节的随机密钥(rps_hash_key),计算得到 32 位哈希值。
  4. 取模映射: 将哈希值与 rps_cpus 掩码进行运算,得到目标 CPU 编号。
注意事项: 如果 skb 中已经包含硬件 RSS 计算的哈希值(skb->hash 有效),RPS 会直接复用该哈希值,避免重复计算。这要求 NIC 驱动支持 NETIF_F_RXHASH 特性。
RPS 哈希计算与 CPU 映射流程 提取四元组 构造字节数组 Toeplitz 哈希计算 32 位哈希值 取模 CPU 掩码 目标 CPU 编号 哈希计算关键点: • 使用 Toeplitz 哈希算法,与硬件 RSS 兼容,保证同一流的数据包始终映射到同一 CPU。 • 哈希密钥(rps_hash_key)可配置,默认随机生成,用于避免哈希冲突和侧信道攻击。

4.3 RPS 数据结构与配置接口

RPS 的核心数据结构是 rps_map,它定义了每个 RX 队列对应的 CPU 集合。系统管理员通过 sysfs 接口配置 RPS 映射。

4.3.1 核心数据结构

数据结构 定义位置 作用
rps_map include/linux/netdevice.h 存储每个 RX 队列对应的 CPU 位图,决定数据包可以分发到哪些 CPU
rps_dev_flow include/linux/netdevice.h 记录每个流(flow)当前使用的 CPU,用于 RFS(Receive Flow Steering)的后续优化
softnet_data net/core/dev.c 每个 CPU 一个实例,包含 backlog 队列(input_pkt_queue)和 RPS 处理统计信息

4.3.2 配置接口

RPS 的配置通过 /sys/class/net/<interface>/queues/rx-<n>/rps_cpus 文件进行。该文件接受一个十六进制 CPU 位图,表示允许处理该 RX 队列数据包的 CPU 集合。

# 查看 eth0 的 RX 队列 0 的 RPS 配置
cat /sys/class/net/eth0/queues/rx-0/rps_cpus

# 将 RX 队列 0 的数据包分发到 CPU 0-3(位图:0x0f)
echo "0f" > /sys/class/net/eth0/queues/rx-0/rps_cpus

# 将 RX 队列 1 的数据包分发到 CPU 4-7(位图:0xf0)
echo "f0" > /sys/class/net/eth0/queues/rx-1/rps_cpus
配置建议: 对于多队列 NIC,建议将每个 RX 队列的 RPS CPU 集合设置为所有可用的 CPU,以实现最大程度的负载均衡。例如,8 核 CPU 可以设置为 ff(位图 0xff)。

4.4 RPS 性能影响与适用场景

RPS 在特定场景下能显著提升网络性能,但也存在一定的开销。以下是 RPS 的优缺点对比:

维度 优势 劣势
CPU 利用率 将软中断负载分散到多个 CPU,避免单核 100% 占用 增加 CPU 间数据包传递(跨 CPU 队列操作),引入缓存失效
吞吐量 在硬件队列数少于 CPU 数时,显著提升整体吞吐量 在硬件队列数等于 CPU 数时,RPS 可能成为瓶颈(软件分发开销)
延迟 避免单核软中断堆积,降低尾延迟 增加数据包在 backlog 队列中的等待时间(入队 + 出队)
适用场景 低端 NIC(2-4 队列)配合多核 CPU(16-64 核) 高端 NIC(64+ 队列)且 CPU 核数较少时,建议关闭 RPS
性能调优经验: 在 40G/100G 网卡场景下,如果 NIC 支持 64 个以上 RX 队列,建议关闭 RPS(设置 rps_cpus = 0),直接依赖硬件 RSS 进行负载均衡。RPS 更适合 1G/10G 网卡或虚拟化环境(如 KVM 虚拟网卡)。

4.5 RPS 与硬件 RSS 的协同工作

RPS 并不是硬件 RSS 的替代品,而是补充。两者可以协同工作,形成两级负载均衡:

  • 第一级(硬件 RSS): NIC 根据哈希值将数据包分配到不同的硬件 RX 队列,每个队列触发独立的 MSI-X 中断。
  • 第二级(软件 RPS): 在中断处理完成后,RPS 根据哈希值将数据包从硬件队列对应的 CPU 分发到其他 CPU 的 backlog 队列。

这种两级架构的优势在于:硬件 RSS 负责中断层面的负载均衡,RPS 负责软中断处理层面的负载均衡,两者互补,最大化 CPU 利用率。

RPS 与硬件 RSS 协同架构 NIC 硬件 RSS RX Queue 0 RX Queue 1 RX Queue 2 RPS 软件分发 CPU-0 软中断 CPU-1 软中断 CPU-2 软中断 两级负载均衡说明: • 第一级(硬件 RSS):NIC 根据哈希值将数据包分配到不同的 RX 队列,每个队列触发独立中断。 • 第二级(软件 RPS):中断处理完成后,RPS 根据哈希值将数据包分发到多个 CPU 的 backlog 队列。 • 协同效果:硬件 RSS 解决中断分配问题,RPS 解决软中断处理负载均衡问题,两者互补。