中断类型
    异常:又叫同步中断,由cpu产生,只有在一条指令终止后CPU才发出中断;取决于eip的值,又可分为故障fault,陷阱trap和中止abort;
    中断:硬件外设依照CPU时钟信号随机产生的;分为 可屏蔽和不可屏蔽;
    两者分别又称为软件中断(不是软中断)和硬件中断,本文只描述后一种;

    硬件中断的处理分为两个阶段,为top half和bottom half;
    上半部只完成一些比较紧急和必要的功能,比如对中断的到达进行响应确认,最好情况下(SA_INTERRUPT),同级其他中断被屏蔽(锁定当前中断线),最坏情况下,当前CPU所有中断被屏蔽;
    下半部则是完成其他那些可以延缓处理的部分,在下半部的执行期间其他CPU可以同时响应软中断。

    下半部最常见为软中断softirq和tasklet,softirq为可延迟函数,tasklet建立在其上;
    linux 2.6提供6个softirq,而tasklet可使用HI_SOFTIRQ和TASKLET_SOFTIRQ,

    以网卡处理为例:
    1 从网卡硬件的缓冲区中把网卡收到的网络数据包复制到系统内存中;
    2 对这个数据包进行 TCP/IP 协议栈的处理;
    3 通过 socket将数据包传入用户空间。
    如果不分段,在数据传入用户空间之前,所有这些处理都会在中断服务程序中完成,在这段时间内,CPU将不再响应网卡发来的其他中断,网卡将因为自身缓存的不足而丢失数据。

    内核线程
    ksoftirqd
    每个CPU各有一个,软中断可以自我激活,对于比较繁华的业务譬如网卡数据包泛滥,可能会耗尽CPU;
    执行期间产生的新的软中断,可以有两种选择:1 忽略,等到下一次时钟中断才执行;2 反复检查并执行,可能导致用户态程序一直等待;
    ksfotirqd试图找到一个平衡点,若已经执行的软中断又被激活,do_softirq()唤醒该内核线程并终止当前执行,允许用户程序抢占执行(内核线程优先级较低);

    kirqd
    kirqd周期性调用do_irq_balance(),跟踪最近时间间隔内每个CPU的中断次数,若分布不平衡则尝试转移IRQ;
    可通过service irqbalance stop关闭;

    嵌套执行
    内核控制路径可以任意嵌套,即中断可以嵌套,前提是中断处理程序不能阻塞, 即运行期间不可发生进程切换;。
    内核控制路径:中断切换需要在内核态堆栈保存程序计数器的当前值(eip和cs寄存器),其执行的代码不是一个进程(比进程切换轻量级);

    CPU affinity
    每个能发出中断的外设都有一条IRQ输出线,
    I/O APIC包含中断重定向表和可编程寄存器,通过修改中断重定向表,可以把中断信息发生到特定CPU,即CPU affinity;
    每个CPU都有一个本地的APIC,本地APIC都连接到一个外部的I/OAPIC,设备的IRQ线连接到I/OAPIC。I/OAPIC接收到中断信号,根据一定的算法,再将中断分配给其中某个本地APIC。也可以通过配置指定某个中断分配给某个CPU。

    软中断负载均衡与RPS
    对于多队列网卡,可直接对网卡接收队列设置CPU affinity,单队列网卡可通过软件先模拟出多队列,然后对每个队列设置affinity,此即为RPS;
    RPS 全称是 Receive Packet Steering, 这是Google工程师 Tom Herbert (therbert@google.com )提交的内核补丁;
    原理:
    linux现在网卡的驱动支持两种模式,一种是NAPI,一种是非NAPI模式,这两种模式的区别:
    在NAPI中,中断收到数据包后调用__napi_schedule调度软中断,然后软中断处理函数中会调用注册的poll回掉函数中调用netif_receive_skb将数据包发送到3层,没有进行任何的软中断负载均衡。
    在非NAPI中,中断收到数据包后调用netif_rx,这个函数会将数据包保存到input_pkt_queue,然后调度软中断,这里为了兼容 NAPI的驱动,他的poll方法默认是process_backlog,最终这个函数会从input_pkt_queue中取得数据包然后发送到3层。
    不管是NAPI还是非NAPI的话都无法做到软中断的负载均衡,因为软中断此时都是运行在在硬件中断相应的cpu上。也就是说如果始终是cpu0相应网卡的硬件中断,那么始终都是cpu0在处理软中断,而此时cpu1就被浪费了,因为无法并行的执行多个软中断。
    google的这个patch的基本原理是这样的,根据数据包的源地址,目的地址以及目的和源端口计算出一个hash值,然后根据这个hash值来选择软中断运行的cpu,从上层来看,也就是说将每个连接和cpu绑定,并通过这个 hash值,来均衡软中断在多个cpu上。

    实现方式
    在netif_receive_skb()之后还有很多处理工作,也是在软中断上下文中,而且处理工作是和进程无关的。这样就可以把后续的处理工作移到其它的CPU上执行,这也是网络协议栈的软中断的负载均衡的基本思想;
    RPS的实现流程:将数据包加入其它CPU的接收队列sd->input_pkt_queue,并激活其它CPU的NAPI结构sd->backlog,则其它CPU将会在自己的软中断中执行process_backlog,process_backlog将会接收sd->input_pkt_queue队列中的所有数据包,并调用__netif_receive_skb()执行后续工作。
    http://blog.chinaunix.net/uid-233938-id-3362368.html
    RPS也有两种实现方式:
    1 每队列绑定到1个CPU
    /sys/class/net/eth0/queues/rx-0/rps_cpus 00000001
    /sys/class/net/eth0/queues/rx-1/rps_cpus 00000002
    /sys/class/net/eth0/queues/rx-2/rps_cpus 00000004
    /sys/class/net/eth0/queues/rx-3/rps_cpus 00000008
    /sys/class/net/eth0/queues/rx-4/rps_cpus 00000010
    /sys/class/net/eth0/queues/rx-5/rps_cpus 00000020
    /sys/class/net/eth0/queues/rx-6/rps_cpus 00000040
    /sys/class/net/eth0/queues/rx-7/rps_cpus 00000080
    2 每队列绑定到多个或所有CPU
    /sys/class/net/eth0/queues/rx-0/rps_cpus 000000ff
    /sys/class/net/eth0/queues/rx-1/rps_cpus 000000ff
    /sys/class/net/eth0/queues/rx-2/rps_cpus 000000ff
    /sys/class/net/eth0/queues/rx-3/rps_cpus 000000ff
    /sys/class/net/eth0/queues/rx-4/rps_cpus 000000ff
    /sys/class/net/eth0/queues/rx-5/rps_cpus 000000ff
    /sys/class/net/eth0/queues/rx-6/rps_cpus 000000ff
    /sys/class/net/eth0/queues/rx-7/rps_cpus 000000ff

    RFS
    http://blog.chinaunix.net/uid-233938-id-3362368.html
    全称是 Receive Flow Steering, 这也是Tom提交的内核补丁,它是用来配合RPS补丁使用的,是RPS补丁的扩展补丁,它把接收的数据包送达应用所在的CPU上,提高cache的命中率。
    由于RPS只是单纯的把同一流的数据包分发给同一个CPU核来处理了,但是有可能出现这样的情况,即给该数据流分发的CPU核和执行处理该数据流的应用程序的CPU核不是同一个:数据包均衡到不同的cpu,这个时候如果应用程序所在的cpu和软中断处理的cpu不是同一个,此时对于cpu cache的影响会很大。那么RFS补丁就是用来确保应用程序处理的cpu跟软中断处理的cpu是同一个,这样就充分利用cpu的cache。
    不仅由于cache缓存的问题,还有一些其它的原因需要把特征相同的数据包分配给同一个CPU处理,例如:
    TCP的IP包的分段重组问题,一旦乱序就要重传,这种情况下,一个linux主机如果只是作为一台路由器的话,那么进入系统的一个TCP包的不同分段如果被不同的cpu处理并向一个网卡转发了,那么同步问题会很麻烦的,如果你不做同步处理,那么很可能后面的段被一个cpu先发出去了,那么在真正的接收方接收到乱序的包后就会请求重发,这是不希望的,因此还是一个cpu串行处理好。

    现在再回过头来看储霸的帖子,感觉轻松不少;http://blog.yufeng.info/archives/2037
    1 某个CPU的软中断偏高:mpstat;
    2 调查是谁惹的祸:stap监控softirq.entry,workqueue.execute和irq_handler.entry,分别为软中断,工作队列和irq中断线;确定是网卡引起的;
    3 解决方案:1 换用多队列网卡 + SMP affinity;2 采用RPS软件模拟多队列;

    原文地址 http://blog.itpub.net/15480802/viewspace-1408527/