中斷處理中不能睡眠的原因

這個問題實際上是一個老生常談的問題,答案也很簡單,Linux軟中斷上下文中是不能睡眠的,原因在於Linux的軟中斷實現上下文有可能是中斷上下文,如果在中斷上下文中睡眠,那麼會導致Linux無法調度,直接的反應是系統KernelPanic,並且提示dequeue_task出錯。所以,在軟中斷上下文中,我們不能使用信號量等可能導致睡眠的函數,這一點在編寫IO回調函數時需要特別注意。在最近的一個項目中,我們在dm-io的callback函數中去持有semaphore訪問競爭資源,導致了系統的kernelpanic。其原因就在於dm-io的回調函數在scsi soft irq中執行,scsi softirq是一個軟中斷,其會在硬中斷髮生之後被執行,執行上下文爲中斷上下文。

  中斷上下文中無法睡眠的原因大家一定很清楚,原因在於中斷上下文不是一個進程上下文,其沒有一個專門用來描述CPU寄存器等信息的數據結構,所以無法被調度器調度。如果將中斷上下文也設計成進程上下文,那麼調度器就可以對其進行調度,如果在開中斷的情況下,其自然就可以睡眠了。但是,如果這樣設計,那麼中斷處理的效率將會降低。中斷(硬中斷、軟中斷)處理都是些耗時不是很長,對實時性要求很高,執行頻度較高的應用,所以,如果採用一個專門的後臺daemon對其處理,顯然並不合適。

  Linux對中斷進行了有效的管理,一箇中斷髮生之後,都會通過相應的中斷向量表獲取該中斷的處理函數。在Linux操作系統中都會調用do_IRQ這個函數,在這個函數中都會執行__do_IRQ(),__do_IRQ函數調用該中斷的具體執行函數。在執行過程中,該函數通過中斷號找到具體的中斷描述結構irq_desc,該結構對某一具體硬件中斷進行了描述。在irq_desc結構中存在一條鏈表irqaction,這條鏈表中的某一項成員都是一箇中斷處理方法。這條鏈表很有意思,其實現了中斷共享,例如傳統的PCI總線就是採用共享中斷的方法,該鏈表中的一個節點就對應了一個PCI設備的中斷處理方法。在PCI設備驅動加載時,都需要註冊本設備的中斷處理函數,通常會調用request_irq這個函數,通過這個函數會構造一個具體的irqaction,然後掛接到某個具體irq_desc的action鏈表下,實現中斷處理方法的註冊。在__do_IRQ函數中會通過handle_IRQ_event()函數遍歷所有的action節點,完成中斷處理過程。到目前爲止,中斷處理函數do_IRQ完成的都是上半部的工作,也就是設備註冊的中斷服務程序。在中斷上半部中,通常都是關中斷的,基本都是完成很簡單的操作,否則將會導致中斷的丟失。耗時時間相對較長,對實時性要求不是最高的應用都會被延遲處理,都會在中斷下半部中執行。所以,在中斷上半部中都會觸發軟中斷事件,然後執行完畢,退出服務。

  __do_IRQ完成之後,返回到do_IRQ函數,在該函數中調用了一個非常重要的函數irq_exit(),在該函數中調用invoke_softirq(),invoke_softirq調用do_softirq()函數,執行軟中斷的操作。此時,程序的執行環境還是中斷上下文,但是與中斷上半部不同的是,軟中斷執行過程中是開中斷的,能夠被硬中斷而中斷。所以,如果用戶的程序在軟中斷中睡眠,操作系統該如何調度呢?只有kernelpanic了。另外,軟中斷除了上述執行點之外,還有其他的執行點,在內核中還有一個軟中斷的daemon處理軟中斷事務,驅動程序也可以自己觸發一個軟中斷事件,並且在軟中斷的daemon上下文中執行。但是硬中斷觸發的事件都不會在這個daemon的上下文中執行,除非修改Linux中的do__IRQ代碼。

  上述對軟中斷的執行做了簡要分析,我對Linux中的硬中斷管理機制做了一些代碼分析,這一塊代碼量不是很大,可移植性非常的好~~建議大家閱讀,對我上述的分析和理解存在什麼不同意見,歡迎大家討論。


>1,中斷處理應該儘可能快的完成。因爲中斷不及時處理,外部設備的利用率嚴重降低。
>2,中斷處理程序中如果睡眠,喚醒的時候會把中斷處理過程中用到的系統資源記賬到它借用內核棧的進>程中去。但是這在很多時候是不合理的。因爲中斷很多時候不和它打斷的進程有關聯。
這樣的種種考慮,這樣做很不合理.
發佈了11 篇原創文章 · 獲贊 6 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章