怎么理解Linux软中断?

馋奶兔 提交于 2019-12-24 12:14:55

 

1. 怎么去理解

前面博客简单说过中断的含义,中断是系统用来响应硬件设备请求的一种机制,会打断进程的正常调度和运行,然后调用内核中的中断处理程序来响应硬件设备的请求。

为什么要有中断?其实中断是一种异步的事件处理机制,可以提高系统的并发处理能力。由于中断处理程序会打断进程的运行,特别是还会临时关闭中断,会导致上一次中断处理程序完成以前,其他中断都不能响应,那么这种情况下中断也可能会丢失,所以为了减少对进程的调度和运行的影响,中断处理程序要尽可能快的执行。

在Linux中,为了解决中断处理程序运行时间过长或者中断丢失的问题,将中断分为两个部分,上半部和下半部:

  • 上半部用来快速处理中断,他在中断禁止的模式下运行,主要处理跟硬件紧密相关的或者时间敏感的工作。
  • 下半部用来延迟处理上半部未完成的工作,通常以内核线程的方式运行,并且每个CPU对应一个软中断内核线程,名字为“ksoftirqd/CPU编号”。另外,软中断不仅仅是刚刚说的硬件设备中断处理程序的下半部,一些内核的自定义事件也是,比如网络收发、定时、内核调度和RCU(Read-Copy Update的缩写,Linux内核最常用的锁之一)等。

举个例子来帮助理解,网卡收包:

网卡收包,会通过硬中断的方式,通知内核有数据到达,内核就应该调用中断处理程序来响应它。这里上半部,就是快速处理,把网卡的数据读到内存中,更新一下硬件寄存器的状态,再发送一个软中断信号,通知下半部作进一步处理。下半部被软中断信号唤醒后,需要从内存中找到的网络数据,按照网络协议栈,逐层解析和处理,直到把它送到应用程序。

2. 查看软中断和内核线程

proc文件系统,是一种Linux内核空间和用户空间的交互的机制,可以用来查看内核的数据结构,或者动态修改内核配置。其中:

  • /proc/softirqs提供软中断的运行情况;
  • /proc/interrupts提供硬中断的运行情况。

$ cat /proc/softirqs
                    CPU0       CPU1
          HI:          0          0
       TIMER:     811613    1972736
      NET_TX:         49          7
      NET_RX:    1136736    1506885
       BLOCK:          0          0
    IRQ_POLL:          0          0
     TASKLET:     304787       3691
       SCHED:     689718    1897539
     HRTIMER:          0          0
         RCU:    1330771    1354737

从这个文件你可以看到软中断的10种类型和各种类型在不同CPU上的累计运行次数。比如:NET_RX,网络接收中断。NET_TX,网络发送中断。正常情况下,每种类型,在不同CPU上累计次数应该差不多,但也有例外。比如TASKLET,最常用的软中断实现机制,每个TASKLET运行一次就会结束,并且只在调用它的函数所在的CPU上运行。因此,这会导致调度的不均衡,更甚的会有性能限制。

使用命令:ps aux | grep ksoftorqd查看到内核线程的运行状态:


$ ps aux | grep softirq
root         7  0.0  0.0      0     0 ?        S    Oct10   0:01 [ksoftirqd/0]
root        16  0.0  0.0      0     0 ?        S    Oct10   0:01 [ksoftirqd/1]

注意,线程名有个中括号,说明ps无法获取它们的命令行参数,一般的像这种名字括在中括号中,就是内核线程。

3. 案例:软中断CPU使用率升高,怎么办?

在Linux中,当软中断事件频率过高时,内核线程也会因为CPU使用率过高,而导致软中断处理不及时,进而引发网络收发延迟,调度缓慢等性能问题。软中断CU使用率过高也是一种常见的性能问题。这里模拟一个SYN-FLOOD攻击场景:

                                                     

这里要使用三个工具:sar、hping3、tcpdump

  • sar是系统活动报告工具,即可以实时查看系统的当前活动,又可以配置保存和报告历史统计数据。
  • hping3是一个可以构造TCP/IP协议数据包的工具,可以对系统进行安全审计、防火墙测试等。
  • tcpdump网络抓包工具,用来分析各种网络问题。

# -S参数表示设置TCP协议的SYN(同步序列号),-p表示目的端口为80
# -i u100表示每隔100微秒发送一个网络帧
# 注:如果你在实践过程中现象不明显,可以尝试把100调小,比如调成10甚至1
$ hping3 -S -p 80 -i u100 192.168.0.30

在另一个终端操作系统的时候,发现系统相应明显变慢,即便敲个回车反应都很慢。


# top运行后按数字1切换到显示所有CPU
$ top
top - 10:50:58 up 1 days, 22:10,  1 user,  load average: 0.00, 0.00, 0.00
Tasks: 122 total,   1 running,  71 sleeping,   0 stopped,   0 zombie
%Cpu0  :  0.0 us,  0.0 sy,  0.0 ni, 96.7 id,  0.0 wa,  0.0 hi,  3.3 si,  0.0 st
%Cpu1  :  0.0 us,  0.0 sy,  0.0 ni, 95.6 id,  0.0 wa,  0.0 hi,  4.4 si,  0.0 st
...

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
    7 root      20   0       0      0      0 S   0.3  0.0   0:01.64 ksoftirqd/0
   16 root      20   0       0      0      0 S   0.3  0.0   0:01.97 ksoftirqd/1
 2663 root      20   0  923480  28292  13996 S   0.3  0.3   4:58.66 docker-containe
 3699 root      20   0       0      0      0 I   0.3  0.0   0:00.13 kworker/u4:0
 3708 root      20   0   44572   4176   3512 R   0.3  0.1   0:00.07 top
    1 root      20   0  225384   9136   6724 S   0.0  0.1   0:23.25 systemd
    2 root      20   0       0      0      0 S   0.0  0.0   0:00.03 kthreadd
...

从top输出,咋一看,是看不出什么问题的,但仔细一看,两个CPU的使用率只有3.3%、4.4%,但都是si即软中断上,从下面的进程列表看,%CPU虽然不高,但都是软中断进程ksoftirqd,猜测软中断有点可疑。可以查看/proc/softirqs/文件内容,但是这个文件的内容是自开机以来的累计值,所以参考价值不大,我们需要看的是一段时间的情况:


$ watch -d cat /proc/softirqs
                    CPU0       CPU1
          HI:          0          0
       TIMER:    1083906    2368646
      NET_TX:         53          9
      NET_RX:    1550643    1916776
       BLOCK:          0          0
    IRQ_POLL:          0          0
     TASKLET:     333637       3930
       SCHED:     963675    2293171
     HRTIMER:          0          0
         RCU:    1542111    1590625

通过以上结果你会发现,NET_RX(网络接收)、SCHED(内核调度)、RCU(RCU锁)几个不停变化。其中,NET_RX变化最快,其他几个都是,是保证Linux调度、时钟和临界保护区这些工作正常运行所必需的,有变化也是正常。接下来从网络接收软中断着手,使用SAR工具:


# -n DEV 表示显示网络收发的报告,间隔1秒输出一组数据
$ sar -n DEV 1
15:03:46        IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil
15:03:47         eth0  12607.00   6304.00    664.86    358.11      0.00      0.00      0.00      0.01
15:03:47      docker0   6302.00  12604.00    270.79    664.66      0.00      0.00      0.00      0.00
15:03:47           lo      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
15:03:47    veth9f6bbcd   6302.00  12604.00    356.95    664.66      0.00      0.00      0.00      0.05

rxpck/s 和 txpck/s:每秒收发的网络帧数,也就是PPS。

rxkB/s 和 txkB/s:每秒收发的千字节数,也就是BPS。

很显然,eth0的收包个数或者字节数明显比发包大,计算一下,664 * 1024 / 12607 = 54字节,很小的网络帧。接下来可以用tcpdump抓取这个网卡的包:


# -i eth0 只抓取eth0网卡,-n不解析协议名和主机名
# tcp port 80表示只抓取tcp协议并且端口号为80的网络帧
$ tcpdump -i eth0 -n tcp port 80
15:11:32.678966 IP 192.168.0.2.18238 > 192.168.0.30.80: Flags [S], seq 458303614, win 512, length 0
...

Flags [S]表示一个SYN包,再加上前面sar发现的PPS 12000的收包,可以确认这是SYN FLOOD 攻击。

 

以上是学习极客时间专栏(倪朋飞:Linux性能优化实战)的个人总结

https://time.geekbang.org/column/intro/140

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!