网卡中断负载均衡

1. CPU 的 Exception

  1. interrupt 中断,硬件发起,异步的(即独立于CPU),下面3种都是同步的(必须由CPU执行某条指令触发);这是本文关注的重点。

  2. fault,可能可以恢复,处理后返回引起错误的指令,如Page Fault。

  3. abort,无法恢复,不返回。

  4. trap / system call,程序主动发起,调用操作系统服务的方式,返回下一条指令。

Exception 的handler都是运行在内核空间的,CPU必须立即响应 exception,处理过程可以简单地认为按如下方式进行:保存现场 – 执行处理函数 – 恢复现场继续执行。

2. 硬中断和软中断

响应中断时通常会关闭中断,因此处理函数必须快速返回,保证不会丢失其他设备的中断信号。因此通常将中断响应函数划分为两个部分,上半部分就是所谓的硬中断,它只串行地做少量最重要的事以保证快速返回,并在返回前发起对下半部分的调度。

下半部分负责其他耗时的工作,它和硬中断的区别在于它在执行时开中断,且是异步的,即等待 CPU 在某个合适的时间点执行,不能保证在硬中断之后立刻被执行。

Linux对“下半部分”的实现有3种方式:softirq/tasklet/bottom half,统称为deferrable functions。其中,tasklet是基于softirq实现的,而bottom half又是基于tasklet实现的。实际上在很多场景下,都使用 softirq(软中断)这个术语来描述所有的deferrable functions(中断处理的下半部分)。这三者的区别主要在并发性上:

  1. Softirq Softirqs of the same type can run concurrently on several CPUs
  2. Tasklet Tasklets of different types can run concurrently on several CPUs, but tasklets of the same type cannot
  3. Bottom HalfBottom halves cannot run concurrently on several CPUs.

Linux2.4中仅预定义了4个softirq:

类别 用途
HI_SOFTIRQ 用于实现bottom half
TASKLET_SOFTIRQ 用于实现tasklet
NET_TX_SOFTIRQ 发送网络数据
NET_RX_SOFTIRQ 接收网络数据

softirq的相关函数:

  1. open_softirq(NET_RX_SOFTIRQ,net_rx_action,null):定义softirq和处理函数;
  2. __cpu_raise_softirq(cpu,NET_RX_SOFTIRQ):在指定cpu设置标志位,激活特定类型的softirq,该函数被硬中断调用;默认遵循“谁触发谁执行”的原则,哪个cpu处理硬中断则继续处理发起的softirq
  3. do_softirq():cpu在某些特定的时间点检查自己的softirq标志位,当发现有pending的softirq时则调用该函数处理pending softirq。

可以看到,deferrable functions(大部分语境下的“软中断”)是操作系统模拟硬件中断方式实现异步任务的一种机制,它和硬件中断执行的机制具有本质的区别。

参考:

  1. Softirqs, Tasklets, and Bottom Halves – 对这3类deferrable functions做了详细的介绍
  2. Linux操作系统 - 中断、异常及系统调用 – 国防科技大学操作系统ppt,详细解释了中断/异常的概念及其处理流程,非常推荐
  3. 《深入理解计算机系统》

3. 查看系统的中断情况

3.1 看硬中断

cat /proc/interrupts

从左到右依次是

  1. irq的序号;
  2. 在各自cpu上发生中断的次数;
  3. 可编程中断控制器;
  4. 触发中断的设备名称(这是个啥?)。

其中ath9k是我的无线网卡,eth0是有线网卡(未使用)。

Alt text

机器的cpu为:

Alt text

的确有4个逻辑核心。

3.2 看软中断

cat /proc/softirqs

$ cat /proc/softirqs
                    CPU0       CPU1       CPU2       CPU3       
          HI:          0          0          0          0
       TIMER:     890234     858500     620927     519799
      NET_TX:       3702       3602       2132       1030
      NET_RX:    2257833    1430762    2507556    1331708
       BLOCK:     119552         82       5661         88
BLOCK_IOPOLL:          0          0          0          0
     TASKLET:      42915     332662    1223525     369765
       SCHED:    1081723     796010     342065     241437
     HRTIMER:       4055       4011       2386       2199
         RCU:     789493     766554     705107     624903

也可以用 mpstat 工具对系统做实时监控,查看各个cpu上softirq的百分比:

$ mpstat -P ALL 1 1
Linux 3.5.0-37-generic (anderson-nb)    20130804日     _i686_  (4 CPU)

173435秒  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest   %idle
173436秒  all    7.05    0.00    1.01    0.00    0.00    0.00    0.00    0.00   91.94
1734360   22.22    0.00    1.01    0.00    0.00    0.00    0.00    0.00   76.77
1734361    3.00    0.00    1.00    0.00    0.00    0.00    0.00    0.00   96.00
1734362    3.00    0.00    1.00    0.00    0.00    0.00    0.00    0.00   96.00
1734363    1.00    0.00    1.00    0.00    0.00    0.00    0.00    0.00   98.00

参考:

4. CPU亲缘性 (affinity)

在 SMP 体系结构中,我们可以通过调用系统调用和一组相关的宏来设置 CPU 亲和性(CPU affinity),将一个或多个进程绑定到一个或多个处理器上运行。同样的,也可以为硬中断设置亲和性,将一个或多个中断源绑定到特定的 CPU 上运行。方法是修改/proc/irq/$IRQ序号/smp_affinity,如下所示:

$ cat /proc/irq/75/smp_affinity 
00000001

表示序号为75的设备(这个场景中是有线网卡eth0)的硬中断由第一个cpu负责处理,可以修改这个参数,让它使用多个cpu。

参数的具体含义:

Binary       Hex 
CPU 0    0001         1 
CPU 1    0010         2
CPU 2    0100         4
CPU 3    1000         8

参数是一个10进制的值,cpu n == 2 的 n 次方。

例如,用cpu0和cpu2:

Binary       Hex 
CPU 0    0001         1 
CPU 2    0100         4
-----------------------
both     0101         5

全用则等于 f:

Binary       Hex 
CPU 0    0001         1 
CPU 1    0010         2
CPU 2    0100         4
CPU 3    1000         8
-----------------------
both     1111         f

通过修改对应smp_affinity文件设置 CPU 亲缘性:

echo f > /proc/irq/75/smp_affinity

5. 网卡中断负载均衡

有时一个网卡的所有硬中断都会被同一个CPU响应,根据“谁触发谁执行”的原则,之后触发的软中断也是由该cpu执行的,这会导致无法充分利用多核cpu,此时我们可以用一些手段将中断均匀地平摊到每个核心,利用相对富余的cpu来提升网络吞吐量。

办法有两种:

5.1 硬中断负载均衡:

如前文所述修改 smp_affinity 文件。

以下是在我本机(Ubuntu12.04)做的一个实验。首先cat /proc/interrupts查看中断情况,初始状态如下(只显示无线网卡):

Alt text

接下来修改该设备的中断cpu亲和性,让cpu0负责处理所有的中断:

echo 1 > /proc/irq/17/smp_affinity

开几个网页,看看视频,查看中断,可以看到cpu1/2/3上的中断数没有任何改变:

Alt text

然后再修改affinity,将中断处理转移到其他3个核心上:

echo e > /proc/irq/17/smp_affinity

继续监控,结果如下:

Alt text

cpu0上的中断数量不再变化,1/2/3上处理的中断数一直在增加,符合预期。

5.2 使用RPS实现软中断负载均衡

RPS是google贡献的一个patch,基本原理是:根据数据包的源地址,目的地址以及目的和源端口,计算出一个hash值,然后根据这个hash值来选择软中断运行的cpu, 从上层来看,也就是说将每个连接和cpu绑定,并通过这个hash值,来均衡软中断在多个cpu上。详细可以参见Receive packet steering patch详解,Linux内核 RPS和RFS功能详细测试分析

此外还可以使用irqbalance实现硬中断负载均衡,但是据说效果不好,参见深度剖析告诉你irqbalance有用吗

这一块水太深,不会。

参考:

  1. MYSQL数据库网卡软中断不平衡问题及解决方案
  2. Howto: Enable Receive Packet Steering (RPS) on Linux 2.6.35
    这篇文章提到了应用了RPS后,依然是CPU0在处理所有的硬中断,要看/proc/softirqs才能看到软中断的均衡。
  3. 关于linux系统上软中断只用到一个cpu的问题
  4. Web性能压力测试工具之ApacheBench(ab)详解
  5. Linux网卡中断使单个CPU过载
Loading Disqus comments...
目录