一.簡介
中斷其實就是由硬件或軟件所發送的一種稱為IRQ(中斷請求)的信號。中斷允許讓設備,如鍵盤,串口卡,並口等設備表明它們需要CPU。
一旦CPU接收了中斷請求,CPU就會暫時停止執行正在運行的程序,並且調用一個稱為中斷處理器或中斷服務程序(interrupt service routine)的特定程序。
舉例:
比如說你訂了一份外賣,但是不確定外賣什么時候送到,也沒有別的方法了解外賣的進度,但是,配送員送外賣是不等人的,到了你這兒沒人取的話,就直接走人了。所以你只能苦苦等着,時不時去門口看看外賣送到沒,而不能干其他事情。
不過呢,如果在訂外賣的時候,你就跟配送員約定好,讓他送到后給你打個電話,那你就不用苦苦等待了,就可以去忙別的事情,直到電話一響,接電話、取外賣就可以了。
這里的“打電話”,其實就是一個中斷。沒接到電話的時候,你可以做其他的事情;只有接到了電話(也就是發生中斷),你才要進行另一個動作:取外賣。
中斷其實是一種異步的事件處理機制,可以提高系統的並發處理能力。由於中斷處理程序會打斷其他進程的運行,所以,為了減少對正常進程運行調度的影響,中斷處理程序就需要盡可能快地運行。如果中斷本身要做的事情不多,那么處理起來也不會有太大問題;但如果中斷要處理的事情很多,中斷服務程序就有可能要運行很長時間。
二.具體
方式
中斷服務程序或中斷處理器可以在中斷向量表中找到,而這個中斷向量表位於內存中的固定地址中。中斷被CPU處理后,就會恢復執行之前被中斷的程序。
其實,在機器啟動的時候,系統就已經識別了所有設備,並且也把相應的中斷處理器加載到中斷表中。
下面是請求CPU關注的兩種方式:
- 基於中斷
- 基於輪詢
所有的linux操作系統都是基於中斷驅動的。
當我們在鍵盤上按下一個按鍵時,鍵盤就會對CPU說,一個鍵已經被按下。在這種情況下,鍵盤的IRQ線路中的電壓就會發生一次變化,而這種電壓的變化就是來自設備的請求,就相當於說這個設備有一個請求需要處理。
/proc/interrupts 文件
在linux的機器上,/proc/interrupts這個文件包含有關於哪些中斷正在使用和每個處理器各被中斷了多少次的信息。
硬中斷
對於上文所討論的場景都是屬於硬中斷的例子。硬中斷主要分為兩種類別:
-
非屏蔽中斷(Non-maskable interrupts,即NMI):就像這種中斷類型的字面意思一樣,這種中斷是不可能被CPU忽略或取消的。NMI是在單獨的中斷線路上進行發送的,它通常被用於關鍵性硬件發生的錯誤,如內存錯誤,風扇故障,溫度傳感器故障等。
-
可屏蔽中斷(Maskable interrupts):這些中斷是可以被CPU忽略或延遲處理的。當緩存控制器的外部針腳被觸發的時候就會產生這種類型的中斷,而中斷屏蔽寄存器就會將這樣的中斷屏蔽掉。我們可以將一個比特位設置為0,來禁用在此針腳觸發的中斷。
軟中斷
編程異常通常叫做軟中斷,軟中斷是通訊進程之間用來模擬硬中斷的 一種信號通訊方式
通常,軟中斷是一些對I/O的請求。例如進程在往磁盤寫數據,這些請求會調用內核中可以調度I/O發生的程序。對於某些設備,I/O請求需要被立即處理,而磁盤I/O請求通常可以排隊並且可以稍后處理。
中斷切換
中斷處理程序在響應中斷時,還會臨時關閉中斷。這就會導致上一次中斷處理完成之前,其他中斷都不能響應,也就是說中斷有可能會丟失。
假如你訂了 2 份外賣,一份主食和一份飲料,並且是由 2 個不同的配送員來配送。這次你不用時時等待着,兩份外賣都約定了電話取外賣的方式。但是,問題又來了。
當第一份外賣送到時,配送員給你打了個長長的電話,商量發票的處理方式。與此同時,第二個配送員也到了,也想給你打電話。
但是很明顯,因為電話占線(也就是關閉了中斷響應),第二個配送員的電話是打不通的。所以,第二個配送員很可能試幾次后就走掉了(也就是丟失了一次中斷)。
為了解決中斷處理程序執行過長和中斷丟失的問題,Linux 將中斷處理過程分成了兩個階段,也就是上半部和下半部:
- 上半部用來快速處理中斷,它在中斷禁止模式下運行,主要處理跟硬件緊密相關的或時間敏感的工作。
- 下半部用來延遲處理上半部未完成的工作,通常以內核線程的方式運行。
上半部就是你接聽電話,告訴配送員你已經知道了,其他事兒見面再說,然后電話就可以掛斷了;下半部才是取外賣的動作,以及見面后商量發票處理的動作。
這樣,第一個配送員不會占用你太多時間,當第二個配送員過來時,照樣能正常打通你的電話。
網卡中斷
網卡接收到數據包后,會通過硬件中斷的方式,通知核有新的數據到了。這時,內核就應該調用中斷處理程序來響應它。
對上半部來說,既然是快速處理,其實就是要把網卡的數據讀到內存中,然后更新一下硬件寄存器的狀態(表示數據已經讀好了),最后再發送一個軟中斷信號,通知下半部做進一步的處理。
而下半部被軟中斷信號喚醒后,需要從內存中找到網絡數據,再按照網絡協議棧,對數據進行逐層解析和處理,直到把它送給應用程序。
上半部會打斷 CPU 正在執行的任務,然后立即執行中斷處理程序。而下半部以內核線程的方式執行,並且每個 CPU 都對應一個軟中斷內核線程,名字為 “ksoftirqd/CPU 編號”,比如說, 0 號 CPU 對應的軟中斷內核線程的名字就是ksoftirqd/0。
不過要注意的是,軟中斷不只包括了剛剛所講的硬件設備中斷處理程序的下半部,一些內核自定義的事件也屬於軟中斷,比如內核調度和 RCU 鎖(Read-Copy Update 的縮寫,RCU 是 Linux 內核中最常用的鎖之一)等。
三.中斷查看
/proc/softirqs 提供了軟中斷的運行情況,它是系統運行以來的累積中斷次數。
/proc/interrupts 提供了硬中斷的運行情況。
下面為網站服務器的信息,2核2G。
[root@web3-wiki ~]# cat /proc/softirqs
CPU0 CPU1
HI: 3 0
TIMER: 23415546 24103542(定時中斷)
NET_TX: 5 7(網絡接收中斷)
NET_RX: 39237430 16258579(網絡發送中斷)
BLOCK: 0 0
BLOCK_IOPOLL: 0 0
TASKLET: 77 48
SCHED: 12851436 12960795(內核調度)
HRTIMER: 0 0
RCU: 10190519 10233604(RCU 鎖)
查看這些中斷次數的變化速率,高亮部分是變化部分
watch -d cat /proc/softirqs
注意點:
中斷有不同類別,不同CPU應該分配均勻。
TASKLET 在不同 CPU 上的分布並不均勻。TASKLET 是最常用的軟中斷實現機制,每個 TASKLET 只運行一次就會結束 ,並且只在調用它的函數所在的 CPU 上運行。
因此,使用 TASKLET 特別簡便,當然也會存在一些問題,比如說由於只在一個 CPU 上運行導致的調度不均衡,再比如因為不能在多個 CPU 上並行運行帶來了性能限制。
軟中斷實際上是以內核線程的方式運行的,每個 CPU 都對應一個軟中斷內核線程,這個軟中斷內核線程就叫做 ksoftirqd/CPU 編號。那要怎么查看這些線程的運行狀況呢?
查看
ps aux | grep softirq
這些線程的名字外面都有中括號,這說明 ps 無法獲取它們的命令行參數(cmline)。一般來說,ps 的輸出中,名字括在中括號里的,一般都是內核線程。