上半部與下半部(top half and bottom half)
想像一名救護員抵達急救現場。第一要務是那緊急的幾秒:止血、確保現場安全——無論如何都要立刻完成。徹底的治療——文書作業、後續追蹤、長期照護——則在事後、較平靜的情況下進行。中斷處理常式也以同樣方式拆分。上半部是中斷一觸發就執行的緊急部分;下半部是較慢、較不具時效性的工作,被刻意延後到稍晚才執行。
具體來說,當一個裝置中斷時,核心執行上半部(也就是中斷服務常式本身),這時往往關閉著中斷,所以它必須快如閃電:向裝置確認、把剛抵達的資料抓進緩衝區、並排定其餘工作。接著它返回,迅速重新開啟中斷。被延後的工作——把網路封包沿協定堆疊往上解析、把資料複製給等待中的行程、決定接下來做什麼——則在安全又方便時以下半部執行。Linux 提供數種下半部機制:softirq(高產出率,可同時在數顆 CPU 上執行)、tasklet(較簡單的 softirq,依型別串列化)以及工作佇列(在普通核心執行緒中執行,且與其他兩者不同,被允許入睡)。
為什麼重要:這種拆分正是作業系統在中斷風暴下仍保持回應的方法。如果所有工作都在上半部完成,一張忙碌的網路卡可能讓中斷關閉太久,導致時鐘、鍵盤與其他一切都被餓著。藉由立刻只做最起碼的事、把大部分推給在開啟中斷狀態下執行的下半部,系統就能處理高事件率而不把其他人都鎖在門外。各系統的名稱不同——Windows 上是延後程序呼叫,Linux 上是 softirq 與 tasklet——但概念是普世共通的。
一張網路卡收到一個封包。上半部只是把封包複製進一個核心緩衝區、標記「有待辦工作」,然後在幾微秒內返回。稍後,一個 softirq(下半部)把封包沿 TCP/IP 解析,並把它送到正確的通訊端(socket)。
現在只做緊急的最低限度;把大部分延後到稍晚、在開啟中斷時執行。
上半部不能入睡,且在中斷部分關閉的狀態下執行,所以它必須極小;下半部才是任何真正、可能緩慢的處理該待的地方。把緩慢的工作放進上半部,正是這整套拆分要防止的那種臭蟲。