天天看點

linux 【CPU性能】 系統中的軟中斷使用率升高怎麼辦?

文章目錄

  • ​​1. 回顧​​
  • ​​2. 從“取外賣”看中斷​​
  • ​​3. 軟中斷​​
  • ​​4. 檢視軟中斷和核心線程​​
  • ​​5. 案例​​
  • ​​6. 小結​​

1. 回顧

用一個不可中斷程序的案例,帶你學習了 ​

​iowait​

​(也就是等待 I/O 的 CPU 使用率)升高時的分析方法。這裡你要記住,程序的不可中斷狀态是系統的一種保護機制,可以保證硬體的互動過程不被意外打斷。是以,短時間的不可中斷狀态是很正常的。

但是,當程序長時間都處于不可中斷狀态時,你就得當心了。這時,你可以使用 dstat、pidstat 等工具,确認是不是磁盤 I/O 的問題,進而排查相關的程序和磁盤裝置。

關于磁盤 I/O 的性能問題,你暫且不用專門去背,我會在後續的 I/O 部分詳細介紹,到時候了解了也就記住了。其實除了 iowait,軟中斷(softirq)CPU 使用率升高也是最常見的一種性能問題。接下來的兩節課,我們就來學習軟中斷的内容,我還會以最常見的反向代理伺服器 Nginx 的案例,帶你分析這種情況。

2. 從“取外賣”看中斷

中斷是系統用來響應硬體裝置請求的一種機制,它會打斷程序的正常排程和執行,然後調用核心中的中斷處理程式來響應裝置的請求。

你可能要問了,為什麼要有中斷呢?我可以舉個生活中的例子,讓你感受一下中斷的魅力。比如說你訂了一份外賣,但是不确定外賣什麼時候送到,也沒有别的方法了解外賣的進度,但是,配送員送外賣是不等人的,到了你這兒沒人取的話,就直接走人了。是以你隻能苦苦等着,時不時去門口看看外賣送到沒,而不能幹其他事情。不過呢,如果在訂外賣的時候,你就跟配送員約定好,讓他送到後給你打個電話,那你就不用苦苦等待了,就可以去忙别的事情,直到電話一響,接電話、取外賣就可以了。這裡的“打電話”,其實就是一個中斷。沒接到電話的時候,你可以做其他的事情;隻有接到了電話(也就是發生中斷),你才要進行另一個動作:取外賣。這個例子你就可以發現,中斷其實是一種異步的事件處理機制,可以提高系統的并發處理能力。

中斷處理程式會打斷其他程序的運作,是以,為了減少對正常程序運作排程的影響,中斷處理程式就需要盡可能快地運作。如果中斷本身要做的事情不多,那麼處理起來也不會有太大問題;但如果中斷要處理的事情很多,中斷服務程式就有可能要運作很長時間。特别是,中斷處理程式在響應中斷時,還會臨時關閉中斷。這就會導緻上一次中斷處理完成之前,其他中斷都不能響應,也就是說中斷有可能會丢失。那麼還是以取外賣為例。假如你訂了 2 份外賣,一份主食和一份飲料,并且是由 2 個不同的配送員來配送。這次你不用時時等待着,兩份外賣都約定了電話取外賣的方式。但是,問題又來了。當第一份外賣送到時,配送員給你打了個長長的電話,商量發票的處理方式。與此同時,第二個配送員也到了,也想給你打電話。但是很明顯,因為電話占線(也就是關閉了中斷響應),第二個配送員的電話是打不通的。是以,第二個配送員很可能試幾次後就走掉了(也就是丢失了一次中斷)。

3. 軟中斷

如果你弄清楚了“取外賣”的模式,那對系統的中斷機制就很容易了解了。事實上,為了解決中斷處理程式執行過長和中斷丢失的問題,Linux 将中斷處理過程分成了兩個階段,也就是上半部和下半部:

  • 上半部用來快速進行中斷,它在中斷禁止模式下運作,主要處理跟硬體緊密相關的或時間敏感的工作。
  • 下半部用來延遲處理上半部未完成的工作,通常以核心線程的方式運作。

比如說前面取外賣的例子,上半部就是你接聽電話,告訴配送員你已經知道了,其他事兒見面再說,然後電話就可以挂斷了;下半部才是取外賣的動作,以及見面後商量發票處理的動作。這樣,第一個配送員不會占用你太多時間,當第二個配送員過來時,照樣能正常打通你的電話。除了取外賣,我再舉個最常見的網卡接收資料包的例子,讓你更好地了解。

網卡接收到資料包後,會通過硬體中斷的方式,通知核心有新的資料到了。這時,核心就應該調用中斷處理程式來響應它。你可以自己先想一下,這種情況下的上半部和下半部分别負責什麼工作呢?

對上半部來說,既然是快速處理,其實就是要把網卡的資料讀到記憶體中,然後更新一下硬體寄存器的狀态(表示資料已經讀好了),最後再發送一個軟中斷信号,通知下半部做進一步的處理。

是以,這兩個階段你也可以這樣了解:

  • 上半部直接處理硬體請求,也就是我們常說的硬中斷,特點是快速執行;
  • 而下半部則是由核心觸發,也就是我們常說的軟中斷,特點是延遲執行。

實際上,上半部會打斷 CPU 正在執行的任務,然後立即執行中斷處理程式。而下半部以核心線程的方式執行,并且每個 CPU 都對應一個軟中斷核心線程,名字為 “ksoftirqd/CPU 編号”,比如說, 0 号 CPU 對應的軟中斷核心線程的名字就是 ksoftirqd/0。

不過要注意的是,軟中斷不隻包括了剛剛所講的硬體裝置中斷處理程式的下半部,一些核心自定義的事件也屬于軟中斷,比如核心排程和 RCU 鎖(Read-Copy Update 的縮寫,RCU 是 Linux 核心中最常用的鎖之一)等。那要怎麼知道你的系統裡有哪些軟中斷呢?

4. 檢視軟中斷和核心線程

不知道你還記不記得,前面提到過的 proc 檔案系統。它是一種核心空間和使用者空間進行通信的機制,可以用來檢視核心的資料結構,或者用來動态修改核心的配置。其中:

  • ​/proc/softirqs​

    ​ 提供了軟中斷的運作情況;
  • ​/proc/interrupts​

    ​ 提供了硬中斷的運作情況。

運作下面的指令,檢視 /proc/softirqs 檔案的内容,你就可以看到各種類型軟中斷在不同 CPU 上的累積運作次數:

$ 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      

在檢視 /proc/softirqs 檔案内容時,你要特别注意以下這兩點:

  • 第一,要注意軟中斷的類型,也就是這個界面中第一列的内容。從第一列你可以看到,軟中斷包括了 10 個類别,分别對應不同的工作類型。比如​

    ​NET_RX​

    ​​ 表示網絡接收中斷,而​

    ​NET_TX​

    ​ 表示網絡發送中斷。
  • 第二,要注意同一種軟中斷在不同 CPU 上的分布情況,也就是同一行的内容。正常情況下,同一種中斷在不同 CPU上的累積次數應該差不多。比如這個界面中,NET_RX 在 CPU0 和 CPU1 上的中斷次數基本是同一個數量級,相差不大。

不過你可能發現,​

​TASKLET​

​ 在不同 CPU 上的分布并不均勻。TASKLET 是最常用的軟中斷實作機制,每個 TASKLET 隻運作一次就會結束 ,并且隻在調用它的函數所在的 CPU 上運作。是以,使用 TASKLET 特别簡便,當然也會存在一些問題,比如說由于隻在一個 CPU 上運作導緻的排程不均衡,再比如因為不能在多個 CPU 上并行運作帶來了性能限制。

另外,剛剛提到過,軟中斷實際上是以核心線程的方式運作的,每個 CPU 都對應一個軟中斷核心線程,這個軟中斷核心線程就叫做 ksoftirqd/CPU 編号。那要怎麼檢視這些線程的運作狀況呢?其實用 ps 指令就可以做到,比如執行下面的指令:

$ 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 無法擷取它們的指令行參數(cmline)。一般來說,ps 的輸出中,名字括在中括号裡的,一般都是核心線程

5. 案例

準備:

  • Ubuntu 18.04
  • 機器配置:2 CPU、8 GB 記憶體。
  • 預先安裝 docker、sysstat、sar 、hping3、tcpdump 等工具,比如 apt-get install

    docker.io sysstat hping3 tcpdump

這裡我又用到了三個新工具,​​sar​​、 hping3 和 tcpdump,先簡單介紹一下:

  • sar 是一個系統活動報告工具,既可以實時檢視系統的目前活動,又可以配置儲存和報告曆史統計資料。
  • hping3 是一個可以構造 TCP/IP 協定資料包的工具,可以對系統進行安全審計、防火牆測試等。
  • tcpdump 是一個常用的網絡抓包工具,常用來分析各種網絡問題。

本次案例用到兩台虛拟機,我畫了一張圖來表示它們的關系。

linux 【CPU性能】 系統中的軟中斷使用率升高怎麼辦?

你可以看到,其中一台虛拟機運作 Nginx ,用來模拟待分析的 Web 伺服器;而另一台當作 Web 伺服器的用戶端,用來給 Nginx 增加壓力請求。使用兩台虛拟機的目的,是為了互相隔離,避免“交叉感染”。

接下來,我們打開兩個終端,分别 SSH 登入到兩台機器上,并安裝上面提到的這些工具。

第一個終端,執行下面的指令運作案例,也就是一個最基本的 Nginx 應用:

# 運作Nginx服務并對外開放80端口
$ docker run -itd --name=nginx -p 80:80 nginx      

然後,在第二個終端,使用 curl 通路 Nginx 監聽的端口,确認 Nginx 正常啟動。假設 ​

​192.168.0.30​

​ 是 Nginx 所在虛拟機的 IP 位址,運作 curl 指令後你應該會看到下面這個輸出界面:

$ curl http://192.168.0.30/



Welcome to nginx!
...      

接着,還是在第二個終端,我們運作 ​​hping3​​ 指令,來模拟 Nginx 的用戶端請求:

# -S參數表示設定TCP協定的SYN(同步序列号),-p表示目的端口為80
# -i u100表示每隔100微秒發送一個網絡幀
# 注:如果你在實踐過程中現象不明顯,可以嘗試把100調小,比如調成10甚至1
$ hping3 -S -p 80 -i u100 192.168.0.30      

現在我們再回到第一個終端,你應該發現了異常。是不是感覺系統響應明顯變慢了,即便隻是在終端中敲幾個回車,都得很久才能得到響應?這個時候應該怎麼辦呢?雖然在運作 hping3 指令時,我就已經告訴你,這是一個 ​

​SYN FLOOD​

​ 攻擊,你肯定也會想到從網絡方面入手,來分析這個問題。

不過,在實際的生産環境中,沒人直接告訴你原因。是以,我希望你把 hping3 模拟 ​

​SYN FLOOD​

​ 這個操作暫時忘掉,然後重新從觀察到的問題開始,分析系統的資源使用情況,逐漸找出問題的根源。那麼,該從什麼地方入手呢?

剛才我們發現,簡單的 ​

​SHELL​

​ 指令都明顯變慢了,先看看系統的整體資源使用情況應該是個不錯的注意,比如執行下 top 看看是不是出現了 CPU 的瓶頸。我們在第一個終端運作 top 指令,看一下系統整體的資源使用情況。

# 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
...      

這裡你有沒有發現異常的現象?我們從第一行開始,逐個看一下:

  • 均負載全是 0,就緒隊列裡面隻有一個程序(1 running)。
  • 每個 CPU 的使用率都挺低,最高的 CPU1 的使用率也隻有 4.4%,并不算高。
  • 再看程序清單,CPU 使用率最高的程序也隻有 0.3%,還是不高呀。

那為什麼系統的響應變慢了呢?既然每個名額的數值都不大,那我們就再來看看,這些名額對應的更具體的含義。畢竟,哪怕是同一個名額,用在系統的不同部位和場景上,都有可能對應着不同的性能問題。仔細看 top 的輸出,兩個 CPU 的使用率雖然分别隻有 3.3% 和 4.4%,但都用在了軟中斷上;而從程序清單上也可以看到,CPU 使用率最高的也是軟中斷程序 ksoftirqd。

看起來,軟中斷有點可疑了。根據上一期的内容,既然軟中斷可能有問題,那你先要知道,究竟是哪類軟中斷的問題。停下來想想,上一節我們用了什麼方法,來判斷軟中斷類型呢?沒錯,還是 proc 檔案系統。觀察 ​

​/proc/softirqs​

​ 檔案的内容,你就能知道各種軟中斷類型的次數。

不過,這裡的各類軟中斷次數,又是什麼時間段裡的次數呢?它是系統運作以來的累積中斷次數。是以我們直接檢視檔案内容,得到的隻是累積中斷次數,對這裡的問題并沒有直接參考意義。因為,這些中斷次數的變化速率才是我們需要關注的。那什麼工具可以觀察指令輸出的變化情況呢?

我想你應該想起來了,在前面案例中用過的 ​

​watch​

​ 指令,就可以定期運作一個指令來檢視輸出;如果再加上 -d 參數,還可以高亮出變化的部分,從高亮部分我們就可以直覺看出,哪些内容變化得更快。比如,還是在第一個終端,我們運作下面的指令:

$ 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      

通過 ​

​/proc/softirqs​

​ 檔案内容的變化情況,你可以發現, TIMER(定時中斷)、NET_RX(網絡接收)、SCHED(核心排程)、RCU(RCU 鎖)等這幾個軟中斷都在不停變化。

其中,NET_RX,也就是網絡資料包接收軟中斷的變化速率最快。而其他幾種類型的軟中斷,是保證 Linux 排程、時鐘和臨界區保護這些正常工作所必需的,是以它們有一定的變化倒是正常的。那麼接下來,我們就從網絡接收的軟中斷着手,繼續分析。既然是網絡接收的軟中斷,第一步應該就是觀察系統的網絡接收情況。這裡你可能想起了很多網絡工具,不過,我推薦今天的主人公工具 ​​sar​​ 。

sar 可以用來檢視系統的網絡收發情況,還有一個好處是,不僅可以觀察網絡收發的吞吐量(​

​BPS​

​​,每秒收發的位元組數),還可以觀察網絡收發的 ​

​PPS​

​,即每秒收發的網絡幀數。

# -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      

對于 sar 的輸出界面,我先來簡單介紹一下,從左往右依次是:

  • 第一列:表示報告的時間。
  • 第二列:IFACE 表示網卡。
  • 第三、四列:​

    ​rxpck/s​

    ​​ 和​

    ​txpck/s​

    ​ 分别表示每秒接收、發送的網絡幀數,也就是 PPS。
  • 第五、六列:​

    ​rxkB/s​

    ​​ 和​

    ​txkB/s​

    ​ 分别表示每秒接收、發送的千位元組數,也就是 BPS。後面的其他參數基本接近0,顯然跟今天的問題沒有直接關系,你可以先忽略掉。

我們具體來看輸出的内容,你可以發現:

對網卡 ​

​eth0​

​ 來說,每秒接收的網絡幀數比較大,達到了 12607,而發送的網絡幀數則比較小,隻有 6304;每秒接收的千位元組數隻有 664 KB,而發送的千位元組數更小,隻有 358 KB。

docker0 和 veth9f6bbcd 的資料跟 eth0 基本一緻,隻是發送和接收相反,發送的資料較大而接收的資料較小。這是 Linux 内部網橋轉發導緻的,你暫且不用深究,隻要知道這是系統把 eth0 收到的包轉發給 Nginx 服務即可。具體工作原理,我會在後面的網絡部分詳細介紹。

從這些資料,你有沒有發現什麼異常的地方?

既然懷疑是網絡接收中斷的問題,我們還是重點來看 eth0 :接收的 PPS 比較大,達到 12607,而接收的 BPS 卻很小,隻有 664 KB。直覺來看網絡幀應該都是比較小的,我們稍微計算一下,​

​664*1024/12607 = 54​

​ 位元組,說明平均每個網絡幀隻有 54 位元組,這顯然是很小的網絡幀,也就是我們通常所說的小包問題。

那麼,有沒有辦法知道這是一個什麼樣的網絡幀,以及從哪裡發過來的呢?

使用 tcpdump 抓取 eth0 上的包就可以了。我們事先已經知道, Nginx 監聽在 80 端口,它所提供的 HTTP 服務是基于 TCP 協定的,是以我們可以指定 TCP 協定和 80 端口精确抓包。

接下來,我們在第一個終端中運作 tcpdump 指令,通過 -i eth0 選項指定網卡 eth0,并通過 tcp port 80 選項指定 TCP 協定的 80 端口:

# -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
...      

從 tcpdump 的輸出中,你可以發現​

​192.168.0.2.18238 > 192.168.0.30.80​

​​ ,表示網絡幀從 192.168.0.2 的 18238 端口發送到 192.168.0.30 的 80 端口,也就是從運作 hping3 機器的 18238 端口發送網絡幀,目的為 Nginx 所在機器的 80 端口。​

​Flags [S]​

​ 則表示這是一個 SYN 包。

再加上前面用 ​​sar​​​ 發現的, PPS 超過 12000 的現象,現在我們可以确認,這就是從 ​

​192.168.0.2​

​​ 這個位址發送過來的 SYN FLOOD 攻擊。到這裡,我們已經做了全套的性能診斷和分析。從系統的軟中斷使用率高這個現象出發,通過觀察 ​

​/proc/softirqs​

​​ 檔案的變化情況,判斷出軟中斷類型是網絡接收中斷;再通過 sar 和 tcpdump ,确認這是一個 ​

​SYN FLOOD​

​ 問題。

SYN FLOOD 問題最簡單的解決方法,就是從交換機或者硬體防火牆中封掉來源 IP,這樣 SYN FLOOD 網絡幀就不會發送到伺服器中。

至于 SYN FLOOD 的原理和更多解決思路,你暫時不需要過多關注,後面的網絡章節裡我們都會學到。

6. 小結

Linux 中的中斷處理程式分為上半部和下半部:

  • 上半部對應硬體中斷,用來快速進行中斷。
  • 下半部對應軟中斷,用來異步處理上半部未完成的工作。

Linux 中的軟中斷包括網絡收發、定時、排程、RCU 鎖等各種類型,可以通過檢視 ​

​/proc/softirqs​

​ 來觀察軟中斷的運作情況。

在 Linux 中,每個 CPU 都對應一個軟中斷核心線程,名字是 ksoftirqd/CPU 編号。當軟中斷事件的頻率過高時,核心線程也會因為 CPU 使用率過高而導緻軟中斷處理不及時,進而引發網絡收發延遲、排程緩慢等性能問題。

繼續閱讀