说明

这篇文章前面相关内容都是小林coding文章中的, 我太菜了而且对这些了解也不深,借用大佬的描述来了解软中断。

软中断也是导致CPU利用率过高的一种原因,在高并发情况下,比如lvs和haproxy,这种代理的性能非常高,转发网络包的数据很快,但是有时候CPU没满,性能却上不去,这个就有可能是软中断导致的,因为有的机器软中断只会在其中几个核处理,当跑满这个CPU核时,系统已经没办法接收更多数据了,吞吐量也就上不去。

中断

在计算机中,中断是系统用来响应硬件设备请求的一种机制,操作系统收到硬件的中断请求,会打断正在执行的进程,然后调用内核中的中断处理程序来响应请求。

  • 中断是一种异步的事件处理机制,可以提高系统的并发处理能力

由于中断处理程序会打断其他进程的运行,为了减少对正常进程运行调度的影响,中断处理程序就需要尽可能快地运行
而且,中断处理程序在响应中断时,可能还会「临时关闭中断」,这意味着,如果当前中断处理程序没有执行完之前,系统中其他的中断请求都无法被响应,也就说中断有可能会丢失,所以中断处理程序要短且快

软中断

Linux 系统为了解决中断处理程序执行过长和中断丢失的问题,将中断过程分成了两个阶段,分别是「上半部和下半部分」。

  • 上半部用来快速处理中断,一般会暂时关闭中断请求,主要负责处理跟硬件紧密相关或者时间敏感的事情。
  • 下半部用来延迟处理上半部未完成的工作,一般以「内核线程」的方式运行。

网卡接收数据包

网卡收到网络包后,会通过硬件中断通知内核有新的数据到了,于是内核就会调用对应的中断处理程序来响应该事件,这个事件的处理也是会分成上半部和下半部。
上部分要做到快速处理,所以只要把网卡的数据读到内存中,然后更新一下硬件寄存器的状态,比如把状态更新为表示数据已经读到内存中的状态值。

接着,内核会触发一个软中断,把一些处理比较耗时且复杂的事情,交给「软中断处理程序」去做,也就是中断的下半部,其主要是需要从内存中找到网络数据,再按照网络协议栈,对网络数据进行逐层解析和处理,最后把数据送给应用程序。

所以,中断处理程序的上部分和下半部可以理解为:

  • 上半部直接处理硬件请求,也就是硬中断,主要是负责耗时短的工作,特点是快速执行;
  • 下半部是由内核触发,也就说软中断,主要是负责上半部未完成的工作,通常都是耗时比较长的事情,特点是延迟执行;

还有一个区别,硬中断(上半部)是会打断 CPU 正在执行的任务,然后立即执行中断处理程序,而软中断(下半部)是以内核线程的方式执行,并且每一个 CPU 都对应一个软中断内核线程,名字通常为「ksoftirqd/CPU 编号」,比如 0 号 CPU 对应的软中断内核线程的名字是 ksoftirqd/0

不过,软中断不只是包括硬件设备中断处理程序的下半部,一些内核自定义事件也属于软中断,比如内核调度等、RCU 锁(内核里常用的一种锁)等。

排查软中断导致的性能问题

在Centos中可以使用cat /proc/softirqs这个命令查看软中断的运行情况

[root@localhost ~]# cat /proc/softirqs
                    CPU0       CPU1       CPU2       CPU3       CPU4       CPU5       CPU6       CPU7
          HI:          4          0          0          0          0          0          0          0
       TIMER:  532198655  459491084  471888996  564053345  463175289  446754977 2240971851  764917002
      NET_TX:    1762126      38718      35142      43037      15838      18829     236006 2104955878
      NET_RX:   15538808   16475626   18623151   17157923   11507031   16678401 2123413007   20772910
       BLOCK:    1493868      26037     116512      23542   10995001    3787614        992    3937814
BLOCK_IOPOLL:          0          0          0          0          0          0          0          0
     TASKLET:         69          4          1          3         16          3          1  187326156
       SCHED:  281338291  229366217  229384601  299741463  159036030  154905926  840990582  259952209
     HRTIMER:          0          0          0          0          0          0          0          0
         RCU:  375348322  332454839  344330542  398191959  318119015  311244918 1114199771  507090060
  • 每一个 CPU 都有自己对应的不同类型软中断的累计运行次数
  • 第一列的内容,它是代表着软中断的类型,其中NET_RX 表示网络接收中断NET_TX 表示网络发送中断
  • 从我这台服务器上可以看出CPU6的NET_RX次数很大,CPU7的NET_TX次数很大,这是因为我进行过多次压测,而这2个核就是在处理对应网卡的软中断。

使用命令cat /proc/interrupts 可以查看硬中断的运行情况。

还有最常用的命令 top 可以查看CPU的使用情况,运行top后按1就可以查看每一个cpu核的使用情况, 这是一个正在接收压力的服务器情况,可以看到8个核的利用率

top - 18:27:09 up 100 days,  9:15,  1 user,  load average: 1.11, 0.54, 0.47
Tasks: 196 total,   4 running, 191 sleeping,   0 stopped,   1 zombie
%Cpu0  : 10.8 us, 11.4 sy,  0.0 ni, 77.8 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  : 10.1 us, 12.5 sy,  0.0 ni, 77.4 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu2  : 12.8 us, 14.1 sy,  0.0 ni, 73.2 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu3  :  9.5 us, 11.6 sy,  0.0 ni, 78.9 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu4  :  9.4 us,  4.7 sy,  0.0 ni, 86.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu5  : 16.5 us,  8.4 sy,  0.0 ni, 75.1 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu6  :  2.0 us,  1.7 sy,  0.0 ni, 45.1 id,  0.0 wa,  0.0 hi, 51.2 si,  0.0 st
%Cpu7  :  9.2 us,  5.8 sy,  0.0 ni, 78.6 id,  0.0 wa,  0.0 hi,  6.4 si,  0.0 st
KiB Mem : 15960012 total,   160152 free,  5327740 used, 10472120 buff/cache
KiB Swap: 16777212 total, 16629500 free,   147712 used. 10256480 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 6464 root      20   0 1499768  40612   1668 R  37.2  0.3 198:03.65 nginx
 6465 root      20   0 1503432  41740   1884 S  31.9  0.3 219:19.11 nginx
 6462 root      20   0 1494748  40528   1668 R  28.2  0.3 157:15.04 nginx
 6460 root      20   0 1492084  39168   1700 R  19.6  0.2 107:35.52 nginx
 6463 root      20   0 1489640  38520   1916 S  11.3  0.2  61:29.39 nginx

其中

  • us:用户空间占用CPU百分比(Host.cpu.user)
  • sy:内核空间占用CPU百分比(Host.cpu.system)
  • ni:用户进程空间内改变过优先级的进程占用CPU百分比
  • id:空闲CPU百分比(Host.cpu.idle)
  • wa:等待输入输出的CPU时间百分比
  • hi:硬件中断
  • si:软件中断
  • st:实时

可以看到us sy每个核都有使用,但是使用率不高,而CPU6的si(软中断)很高,有50%多,这是因为发压的数据请求字节数很大,响应字节数很小,而上面我提过这台机器CPU6处理网络接收中断的,所以进入的数据流会很大,也就是会更占CPU。

从这个情况就可以扩展想一下,整体的CPU我们可以看到利用率不高,因为每个核的空闲百分比都很大(70%多),但是软中断的CPU6则利用率有50%多,所以这个软中断就很有可能成为性能瓶颈,也就是当请求量增加到一定程度,CPU6会直接被打满,而其他CPU核往往还有大量空闲。

这是部署Nginx的一个情况,HaProxy和LVS等等都一样,都会受到这个限制,所以当发现吞吐量上不去,而CPU利用率也比较低的时候就应该看看是不是软中断导致的

解决方法

如果排查到软中断导致性能问题,解决方法可以从以下几方面考虑

  1. 硬件升级,CPU越好,那么处理速度越快,就能缓解这个问题,但是得加钱。
  2. 网卡多队列、RSS,可以查看是否支持多队列,比如通过命令cat /proc/interrupts,如果是以下这种情况,可以看到有多个核都能处理网卡中断,那么这种性能会高很多。有的博客提到过可以修改参数配置,让网卡中断绑定到不同的核上,但是我试过不行,可能是操作不对或者系统不支持,后面也没深入研究,如果有这种需求可以再找一找相关资料。
        CPU0       CPU1       CPU2       CPU3       CPU4       CPU5       CPU6       CPU7
    35: 1157411688          0          0          0          0          0          0          0  IR-PCI-MSI-edge      eth0-TxRx-0
    36: 2199548159          0          0          0          0          0          0          0  IR-PCI-MSI-edge      eth0-TxRx-1
    37: 2210448541          0          0          0          0          0          0          0  IR-PCI-MSI-edge      eth0-TxRx-2
    38:  954381730          0          0          0          0          0          0          0  IR-PCI-MSI-edge      eth0-TxRx-3
    39: 3991435919          0          0          0          0          0          0          0  IR-PCI-MSI-edge      eth0-TxRx-4
    40: 2197207910          0          0          0          0          0          0          0  IR-PCI-MSI-edge      eth0-TxRx-5
    41: 2685654183          0          0          0          0          0          0          0  IR-PCI-MSI-edge      eth0-TxRx-6
    42: 2153260851          0          0          0          0          0          0          0  IR-PCI-MSI-edge      eth0-TxRx-7
  3. DPDK ,DPDK 抛弃了传统的内核中断,采用轮询模式驱动( poll mode driver,PMD) 的方式直接操作网卡的接收和发送队列,将报文直接拷贝到用户空间,不再经过内核协议栈。这样就不会受到大量中断影响了,性能也会更高,比如可以看美团的MGV,也可以搜一下其他相关的技术。
  4. 使用不同的负载均衡方式,DNS、LVS、硬件负载等等