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.2 RSS:接收端缩放(硬件分发)
RSS(Receive Side Scaling)是网卡硬件实现的多队列分发机制。网卡根据报文头部字段(如IP源/目的地址、TCP/UDP端口号)计算哈希值,取模后映射到指定RX队列。整个过程在硬件层面完成,无需CPU干预。
RSS哈希计算流程
- 提取元组: 从报文头部提取四元组(Src IP, Dst IP, Src Port, Dst Port)或二元组(仅IP地址)。
- 计算哈希: 使用Toeplitz哈希算法(或对称哈希)计算32位哈希值。
- 取模映射: 哈希值对可用队列数取模,得到目标队列索引。
- 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
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(硬件) | RPS(软件) |
|---|---|---|
| 实现位置 | 网卡硬件 | 内核网络栈 |
| 分发粒度 | RX队列级别 | CPU核心级别 |
| CPU开销 | 无(硬件完成) | 有(哈希+IPI) |
| 队列数限制 | 受硬件限制 | 无限制(可到任意CPU) |
| 适用场景 | 多队列网卡 | 单队列网卡或队列数不足 |
| 配置方式 | ethtool | sysfs + sysctl |
4.4 队列与CPU映射策略
队列与CPU的映射关系直接影响中断处理效率和缓存局部性。合理的映射策略应遵循以下原则:
- NUMA亲和性: 将队列中断绑定到与网卡同NUMA节点的CPU核心,避免跨NUMA访问。
- 缓存隔离: 避免将多个队列中断绑定到同一个物理核心的多个超线程上,防止L1/L2缓存竞争。
- 预留核心: 为关键业务或控制平面预留专用CPU核心,避免被中断处理完全占用。
映射策略对比:
| 策略 | 描述 | 优点 | 缺点 |
|---|---|---|---|
| 1:1 映射 | 每个队列绑定到独立物理核心 | 完全并行,无竞争 | 核心消耗大,队列数多时不可行 |
| N:1 映射 | 多个队列共享一个核心 | 节省核心,适合低负载 | 中断竞争,吞吐量下降 |
| NUMA感知映射 | 队列绑定到同NUMA节点核心 | 低延迟,高本地性 | 跨NUMA流量需额外处理 |
| 动态映射 | 根据负载动态调整绑定 | 自适应,灵活 | 实现复杂,需用户态工具 |
下图展示了NUMA感知的队列与CPU映射关系:
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
smp_affinity_list(CPU编号列表)比smp_affinity(十六进制掩码)更直观。
4.6 总结
网卡多队列技术通过硬件(RSS)和软件(RPS)两种机制实现报文并行分发。RSS利用网卡硬件哈希将报文分配到不同RX队列,每个队列拥有独立IRQ,可绑定到不同CPU核心;RPS则在软件层面将单个队列的报文分发到多个CPU核心,弥补硬件队列数不足的问题。队列与CPU的映射策略需综合考虑NUMA亲和性、缓存隔离和负载均衡,是硬中断亲缘性调优的核心环节。