4. 网卡多队列原理:RSS与RPS、多队列网卡硬件架构、队列与CPU映射

网卡多队列技术是现代高性能网络栈的基石。它通过将单个网卡的收发队列拆分为多个并行队列,并利用硬件或软件机制将队列绑定到不同CPU核心,从而消除单核瓶颈、提升报文处理吞吐量。本节将深入剖析多队列网卡的硬件架构、RSS与RPS两种核心分发机制,以及队列与CPU的映射策略。

4.1 多队列网卡硬件架构

传统单队列网卡只有一个RX/TX描述符环,所有报文到达后触发同一个IRQ,由单个CPU核心处理。多队列网卡(Multi-Queue NIC)内部集成多个独立的描述符环(队列),每个队列拥有独立的IRQ线,可被路由到不同CPU核心。

核心硬件组件:
  • RX描述符环(RX Ring): 每个队列对应一个环形缓冲区,存放DMA写入的报文描述符。
  • TX描述符环(TX Ring): 每个队列对应一个发送环形缓冲区。
  • 队列寄存器组: 每个队列拥有独立的控制/状态寄存器,包括中断状态寄存器(ISR)、中断掩码寄存器(IMR)等。
  • 中断向量表(MSI-X Table): 支持MSI-X的网卡可为每个队列分配独立的中断向量,实现IRQ到CPU的精确路由。

下图展示了一个典型的4队列网卡硬件架构,每个队列通过独立的PCIe MSI-X中断连接到不同CPU核心:

多队列网卡硬件架构(4队列 + MSI-X) NIC 芯片(多队列硬件) RX Queue 0 RX Queue 1 RX Queue 2 RX Queue 3 MSI-X Vector 0 MSI-X Vector 1 MSI-X Vector 2 MSI-X Vector 3 CPU 0 Core 0 CPU 1 Core 1 CPU 2 Core 2 CPU 3 Core 3 每个RX队列拥有独立的MSI-X中断向量,可精确路由到指定CPU核心,实现硬件级负载均衡
关键硬件特性: 多队列网卡通过硬件哈希(RSS)将入站报文自动分发到不同RX队列,每个队列的IRQ独立触发,从而将中断处理负载分散到多个CPU核心。现代40G/100G网卡通常支持64~256个队列。

4.2 RSS:接收端缩放(硬件分发)

RSS(Receive Side Scaling)是网卡硬件实现的多队列分发机制。网卡根据报文头部字段(如IP源/目的地址、TCP/UDP端口号)计算哈希值,取模后映射到指定RX队列。整个过程在硬件层面完成,无需CPU干预。

RSS哈希计算流程

  1. 提取元组: 从报文头部提取四元组(Src IP, Dst IP, Src Port, Dst Port)或二元组(仅IP地址)。
  2. 计算哈希: 使用Toeplitz哈希算法(或对称哈希)计算32位哈希值。
  3. 取模映射: 哈希值对可用队列数取模,得到目标队列索引。
  4. DMA入队: 网卡将报文DMA到对应RX队列的描述符环中。

RSS的关键参数通过ethtool配置:

# 查看当前RSS哈希字段和队列数
ethtool -n eth0 rx-flow-hash tcp4

# 设置RSS哈希字段(四元组)
ethtool -N eth0 rx-flow-hash tcp4 sdfn

# 查看RSS队列数(Combined值)
ethtool -l eth0

# 设置RSS队列数(需驱动支持)
ethtool -L eth0 combined 8
RSS局限性: RSS依赖硬件哈希,无法感知上层协议状态(如连接跟踪)。对于封装协议(VXLAN/GRE)或分片报文,哈希可能不均匀。此外,RSS队列数受网卡硬件限制,无法动态调整。

4.3 RPS:接收端包分发(软件分发)

RPS(Receive Packet Steering)是Linux内核在软件层面实现的报文分发机制,用于弥补RSS的不足。当网卡不支持多队列或队列数不足时,RPS可以将单个RX队列中的报文通过软件分发到多个CPU核心处理。

RPS的工作原理:

  • 入口点: 在netif_receive_skb()函数中,对每个报文计算哈希(基于四元组)。
  • CPU映射: 根据/sys/class/net/eth0/queues/rx-0/rps_cpus中的CPU掩码,将报文放入目标CPU的backlog队列。
  • IPI唤醒: 通过核间中断(IPI)唤醒目标CPU,由该CPU的软中断(NET_RX_SOFTIRQ)处理报文。

RPS配置示例:

# 将RX队列0的RPS CPU掩码设置为CPU 0-3(十六进制0xf)
echo f > /sys/class/net/eth0/queues/rx-0/rps_cpus

# 设置RPS流表大小(默认4096)
echo 32768 > /proc/sys/net/core/rps_sock_flow_entries

# 设置每个队列的流表大小
echo 4096 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
RSS vs RPS 对比:
特性RSS(硬件)RPS(软件)
实现位置网卡硬件内核网络栈
分发粒度RX队列级别CPU核心级别
CPU开销无(硬件完成)有(哈希+IPI)
队列数限制受硬件限制无限制(可到任意CPU)
适用场景多队列网卡单队列网卡或队列数不足
配置方式ethtoolsysfs + sysctl

4.4 队列与CPU映射策略

队列与CPU的映射关系直接影响中断处理效率和缓存局部性。合理的映射策略应遵循以下原则:

  • NUMA亲和性: 将队列中断绑定到与网卡同NUMA节点的CPU核心,避免跨NUMA访问。
  • 缓存隔离: 避免将多个队列中断绑定到同一个物理核心的多个超线程上,防止L1/L2缓存竞争。
  • 预留核心: 为关键业务或控制平面预留专用CPU核心,避免被中断处理完全占用。

映射策略对比:

策略描述优点缺点
1:1 映射每个队列绑定到独立物理核心完全并行,无竞争核心消耗大,队列数多时不可行
N:1 映射多个队列共享一个核心节省核心,适合低负载中断竞争,吞吐量下降
NUMA感知映射队列绑定到同NUMA节点核心低延迟,高本地性跨NUMA流量需额外处理
动态映射根据负载动态调整绑定自适应,灵活实现复杂,需用户态工具

下图展示了NUMA感知的队列与CPU映射关系:

NUMA感知的队列与CPU映射 NUMA Node 0 NIC 0(PCIe Slot 0) 队列 0-3 CPU 0(Core 0) 处理队列 0 IRQ CPU 1(Core 1) 处理队列 1 IRQ 本地内存(DMA缓冲区) NUMA Node 1 NIC 1(PCIe Slot 1) 队列 4-7 CPU 2(Core 2) 处理队列 4 IRQ CPU 3(Core 3) 处理队列 5 IRQ 本地内存(DMA缓冲区) NUMA感知映射:NIC 0的队列绑定到NUMA Node 0的CPU核心,NIC 1绑定到NUMA Node 1,避免跨NUMA内存访问

4.5 实战:查看和调整队列映射

以下命令用于查看和调整网卡队列与CPU的映射关系:

# 查看网卡队列数和当前映射
cat /proc/interrupts | grep eth0

# 查看每个队列的IRQ号
ls -l /sys/class/net/eth0/device/msi_irqs/

# 查看当前IRQ亲和性设置
cat /proc/irq/78/smp_affinity
cat /proc/irq/78/smp_affinity_list

# 设置IRQ亲和性(十六进制掩码,CPU 0-3)
echo f > /proc/irq/78/smp_affinity

# 使用irqbalance自动管理(生产环境建议关闭)
systemctl stop irqbalance
systemctl disable irqbalance
最佳实践: 对于高吞吐场景(如DPDK、NFV),建议关闭irqbalance,手动将每个队列的IRQ绑定到独立物理核心。使用smp_affinity_list(CPU编号列表)比smp_affinity(十六进制掩码)更直观。

4.6 总结

网卡多队列技术通过硬件(RSS)和软件(RPS)两种机制实现报文并行分发。RSS利用网卡硬件哈希将报文分配到不同RX队列,每个队列拥有独立IRQ,可绑定到不同CPU核心;RPS则在软件层面将单个队列的报文分发到多个CPU核心,弥补硬件队列数不足的问题。队列与CPU的映射策略需综合考虑NUMA亲和性、缓存隔离和负载均衡,是硬中断亲缘性调优的核心环节。