下面是一台服务器的top后的cpu状态:

SMP IRQ Affinity与IRQ技术_第1张图片 

    si的意思是system interrupt,也就是系统中断。(比如,QQ消息到来时,你就会放下目前不重要的工作去查看QQ消息)

    中断是指由于接收到外围硬件(相对于CPU与内存而言)的异步信号或者来自软件的同步信号而进行相应的硬件/软件处理,外围硬件发给CPU或者内存的异步信号就是硬中断信号 由软件本身发给操作系统内核的中断信号,称之为软中断。通常是由硬中断处理程序或进程调度程序对操作系统内核的中断,也就是我们常说的系统调用(System Call)了。

    为了防止不同的硬件使用相同的中断信号,Linux设计了一套中断请求系统, 使得计算机系统中的每个设备被分配了各自的中断号, 以确保此设备的中断请求的唯一性. 从2.4 内核(2001年)开始, Linux改进了分配特定中断到指定的处理器(或处理器组)的功能. 这被称为SMP IRQ affinity, 它可以控制系统如何响应各种硬件事件. 允许你限制或者重新分配服务器的工作负载, 从而让服务器更有效的工作. 以网卡中断为例,在没有设置SMP IRQ affinity时, 所有网卡中断都关联到CPU0, 这导致了CPU0负载过高,而无法有效快速的处理网络数据包,导致了瓶颈。 通过SMP IRQ affinity技术, 把网卡多个中断分配到多个CPU上,可以分散CPU压力,提高数据处理速度。

    

linux的IRQ(中断号)可以在/proc/interrupt中看到:

SMP IRQ Affinity与IRQ技术_第2张图片

他们的意义如下:

IRQ编号、各个程序再对应cpu核心上发生的si次数、可编程中断控制器(也就是RPS只支持具有IO-APIC的设备)、设备名称


    IRQ全称为Interrupt Request,即是“中断请求”的意思,也就是硬件设备请求CPU处理自己的发送的事件。


    Intel Pentium D 双核处理器2005年5月26日正式发布,此后我们便进入了多核时代。但是,程序的irq依然只是使用单核心,所以谷歌工程师便给内核打了补丁,使得我们可以使用多个核心,这个补丁在2010年8月1日发布的kernel2.6.35上面(RHEL/CentOS等不受内核版本影响,为了保持内核稳定性,他们的低内核版本打入很多高内核版本的新特性)。下面是摘抄内容:

    RPS 全称是 Receive Packet Steering, 这是Google工程师 Tom Herbert ([email protected] )提交的内核补丁, 在2.6.35进入Linux内核. 这个patch采用软件模拟的方式,实现了多队列网卡所提供的功能,分散了在多CPU系统上数据接收时的负载, 把软中断分到各个CPU处理,而不需要硬件支持,大大提高了网络性能。
    RFS 全称是 Receive Flow Steering, 这也是Tom提交的内核补丁,它是用来配合RPS补丁使用的,是RPS补丁的扩展补丁,它把接收的数据包送达应用所在的CPU上,提高cache的命中率。
    这两个补丁往往都是一起设置,来达到最好的优化效果, 主要是针对单队列网卡多CPU环境(多队列多重中断的网卡也可以使用该补丁的功能,但多队列多重中断网卡有更好的选择:SMP IRQ affinity)
原理
    RPS: RPS实现了数据流的hash归类,并把软中断的负载均衡分到各个cpu,实现了类似多队列网卡的功能。由于RPS只是单纯的把同一流的数据包分发给同一个CPU核来处理了,但是有可能出现这样的情况,即给该数据流分发的CPU核和执行处理该数据流的应用程序的CPU核不是同一个:数据包均衡到不同的cpu,这个时候如果应用程序所在的cpu和软中断处理的cpu不是同一个,此时对于cpu cache的影响会很大。那么RFS补丁就是用来确保应用程序处理的cpu跟软中断处理的cpu是同一个,这样就充分利用cpu的cache。
    =>应用RPS之前: 所有数据流被分到某个CPU, 多CPU没有被合理利用, 造成瓶颈
    =>应用RPS之后: 同一流的数据包被分到同个CPU核来处理,但可能出现cpu cache迁跃
    =>应用RPS+RFS之后: 同一流的数据包被分到应用所在的CPU核
必要条件


    对于高级网卡而言,为了提高性能,他们在硬件驱动层面就实现了一个网卡多个irq的情况,但是由于RHEL默认的irqblance功能,他们可能会不均匀的使用cpu核心,这样不仅会浪费多核心cpu,也会造cpu的cs过多。所以我们需要关闭irqblance,然后手动指定irq。下图是已经做过优化的网卡情况:

指定em1-0使用cpu1,em1-1使用cpu2,(cpu0上的统计是没有被清空的原因造成的):

wKiom1St2PeTKtH5AADYFyUNmrM542.jpg    

下面讲实现步骤:

# cat /proc/interrupts
        如上图,用来查看所有的中断信息。

# ls /proc/irq/
        此目录存放了所有interrupts中的irq编号对应的目录
0  10   103  105  107  11  13  15  22  3  5  7  88  9   91  93  default_smp_affinity
1  102  104  106  108  12  14  2   23  4  6  8  89  90  92  94
        default_smp_affinity 此目录的此文件指定了默认如何使用哪颗cpu核心
        
# cd /proc/irq/104/
        让我们就以em1-0做解释吧
# ls
  affinity_hint  em1-0  node  smp_affinity  smp_affinity_list  spurious
# cat smp_affinity 
  0000,00000002
        smp_affinity 存放的是一个位掩码bit,使用16进制表示此irq使用哪颗cpu。
                     而cpu是使用二进制进行过表示的。
                     如果是fffffff,那么我们只关注最后的即可。
                     后面介绍如何转换他们之间的关系。
# cat smp_affinity_list 
  1
        smp_affinity_list 与不带list的功能类似。但是使用的是十进制进行表示使用个cpu,所以可读性更好些。

hex与binary:

https://cs.uwaterloo.ca/~brecht/servers/apic/SMP-affinity.txt
上面的原文链接,
    计算cpu的二进制掩码使用0/1表示,比如原文的4核cpu,使用第0颗cpu时:0001。
    当指定使用第2颗cpu时:0100。当我们要使用第0和第2颗cpu时:0101。
    所以数字的数量表示cpu的数量,而1表示使用那颗cpu核心。而最右边的数字表示第0颗cpu
    
    为了配合smp_affinity,我们还需要将二进制转换为16进制。(程序猿计算器即可)

        二进制  十六进制 
cpu0    0001    1
cpu2    0100    4
cpu0+2  0101    5

上图中em1-0使用的是第cpu1,使用二进制表示:"000000000000000000000010"(24核心) 
转换为16进制就是"2"。
所以我们就需要写入2到smp_affinity
# echo 2 > /proc/irq/104/smp_affinity
        当我们需要使用上面的cpu0+cpu2时,一定要关闭irqblance.

另外还需要注意的是smp_affinity最大可以是ffffffff(8),表示成二进制就是32个1,
也就是说,RPS功能最大可以指定到32个cpu。