為了解決中斷處理程式執行過長和中斷丢失的問題,Linux 将中斷處理過程分成了兩個階段,也就是上半部和下半部:
- 上半部用來快速進行中斷,它在中斷禁止模式下運作,主要處理跟硬體緊密相關的或時間敏感的工作。
- 下半部用來延遲處理上半部未完成的工作,通常以核心線程的方式運作。
舉個最常見的網卡接收資料包的例子,讓你更好地了解。
網卡接收到資料包後,會通過硬體中斷的方式,通知核心有新的資料到了。這時,核心就應該調用中斷處理程式來響應它。你可以自己先想一下,這種情況下的上半部和下半部分别負責什麼工作呢?
對上半部來說,既然是快速處理,其實就是要把網卡的資料讀到記憶體中,然後更新一下硬體寄存器的狀态(表示資料已經讀好了),最後再發送一個軟中斷信号,通知下半部做進一步的處理。
而下半部被軟中斷信号喚醒後,需要從記憶體中找到網絡資料,再按照網絡協定棧,對資料進行逐層解析和處理,直到把它送給應用程式。
是以,這兩個階段你也可以這樣了解:
- 上半部直接處理硬體請求,也就是我們常說的硬中斷,特點是快速執行;
- 而下半部則是由核心觸發,也就是我們常說的軟中斷,特點是延遲執行。
實際上,上半部會打斷 CPU 正在執行的任務,然後立即執行中斷處理程式。而下半部以核心線程的方式執行,并且每個 CPU 都對應一個軟中斷核心線程,名字為 “ksoftirqd/CPU 編号”,比如說, 0 号 CPU 對應的軟中斷核心線程的名字就是 ksoftirqd/0。
不過要注意的是,軟中斷不隻包括了剛剛所講的硬體裝置中斷處理程式的下半部,一些核心自定義的事件也屬于軟中斷,比如核心排程和 RCU 鎖(Read-Copy Update 的縮寫,RCU 是 Linux 核心中最常用的鎖之一)等。
那要怎麼知道你的系統裡有哪些軟中斷呢?
檢視軟中斷和核心線程
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 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]