為什么Linux不能在中斷中睡眠


中斷分析

首先來看中斷的流程:

1.進入中斷處理程序--->  2.保存關鍵上下文----> 3.開中斷(sti指令)---> 
/* 
	硬中斷:對應於1、2、3步驟。
	在這幾個步驟中,所有中斷是被屏蔽的,如果在這個時候睡眠了,操作系統不會收到任何中斷(包括時鍾中斷),系統就基本處於癱瘓狀態(例如調度器依賴的時鍾節拍沒有等等……)
	
	中斷handler會使用被中斷的進程內核堆棧(但不會對它有任何影響,因為handler使用完后會完全清除它使用的那部分堆棧,恢復被中斷前的原貌)。

*/  
4.進入中斷處理程序的 handler--->5.關中斷(cli指令)----> 6.寫EOI寄存器(表示中斷處理完成)----> 7.開中斷。
/* 軟中斷: 對應上的4(當然,准確的說應該是4步驟的后面一點)。這個時候不能睡眠的關鍵是因為上下文:
    大家知道操作系統以進程調度為單位,進程的運行在進程的上下文中,以進程描述符作為管理的數據結構。進程可以睡眠的原因是操作系統可以切換不同進程的上下文,進行調度操作,這些操作都以進程描述符為支持。
 中斷運行在中斷上下文,沒有一個所謂的中斷描述符來描述它,它不是操作系統調度的單位。一旦在中斷上下文中睡眠,首先無法切換上下文(因為沒有中斷描述符,當前上下文的狀態得不到保存),其次,沒有人來喚醒它,因為它不是操作系統的調度單位。
 此外,中斷的發生是非常非常頻繁的,在一個中斷睡眠期間,其它中斷發生並睡眠了,那很容易就造成中斷棧溢出導致系統崩潰。
 */

如果條件滿足了(即:有中斷描述符,並成為調度器的調度單位,棧也不溢出了,理論上是可以做到中斷睡眠的),中斷是可以睡眠的,但會引起很多問題.

例如,你在時鍾中斷中睡眠了,那操作系統的時鍾就亂了,調度器也了失去依據;例如,你在一個IPI(處理器間中斷)中,其它CPU都在死循環等你答復,你卻睡眠了,那其它處理器也不工作了;

例如,你在一個DMA中斷中睡眠了,上面的進程還在同步的等待I/O的完成,性能就大大降低了……還可以舉出很多例子。

所以,中斷是一種緊急事務,需要操作系統立即處理,不是不能做到睡眠,是它沒有理由睡眠。

原因

會導致無法喚醒:
中斷處理的時候,不應該發生進程切換,因為在中斷context中,唯一能打斷當前中斷handler的只有更高優先級的中斷,它不會被進程打斷,如果在中斷context中休眠,則沒有辦法喚醒它,因為所有的wake_up_xxx都是針對某個進程而言的,而在中斷context中,沒有進程的概念,沒有一個task_struct(這點對於softirq和tasklet一樣),因此真的休眠了,比如調用了會導致block的例程,內核幾乎肯定會死。

會導致上下文錯亂:
睡眠函數會調用schedule(),切換進程時,保存當前的進程上下文(CPU寄存器的值、進程的狀態以及堆棧中的內容),以便以后恢復此進程運行。中斷發生后,內核會先保存當前被中斷的進程上下文(在調用中斷處理程序后恢復);但在中斷處理程序里,CPU寄存器的值肯定已經變化了吧(最重要的程序計數器PC、堆棧SP等),如果此時因為睡眠或阻塞操作調用了schedule(),則保存的進程上下文就不是當前的進程context了,所以不可以在中斷處理程序中調用schedule()。

結論

任何os,不管是分時os,還是實時os,不管是什么內核(微內核,或者巨內核),在ISR中都不能進行進程切換。

因為ISR不屬於任何進程,而切換只能發生在進程上下文中。雖然ISR在執行過程中要使用進程的系統堆棧,但那只是借用,堆棧並不屬於isr,而是屬於進程。

也可以從優先級角度來理解。任何進程,不論其優先級多高,也不能高過isr,所以不能剝奪isr對cpu的占有權去運行進程。

Linux是以進程為調度單位的,調度器只看到進程內核棧,而看不到中斷棧。在獨立中斷棧的模式下,如果linux內核在中斷路徑內發生了調度(從技術上講,睡眠和調度是一個意思),那么linux將無法找到“回家的路”,未執行完的中斷處理代碼將再也無法獲得執行機會,因為沒有人能夠喚醒它。

附錄:能不能把中斷設計為允許睡眠

當一個進程A因為中斷被打斷時,中斷處理程序會使用A的內核棧來保存上下文,因為是“搶”的 A 的CPU,而且用了 A 的內核棧,因此中斷應該盡可能快的結束。如果 do_IRQ 時又被時鍾中斷打斷,則繼續在 A 的內核棧上保存中斷上下文,如果發生調度,則 schedule 進 switch_to,又會在 A 的 task_struct->thread_struct 里保存此時時種中斷的上下文。

假如其是在睡眠時被時鍾中斷打斷,並 schedule 的話,假如選中了進程 A,並 switch_to 過去,時鍾中斷返回后則又是位於原中斷睡眠時的狀態,拋開其擾亂了與其無關的進程A的運行不說,這里的問題就是:該如何喚醒之呢??

另外,和該中斷共享中斷號的中斷也會受到影響。

中斷不能睡眠的的最大好處就是可以簡化內核的設計。如果說中斷隨時可以睡眠的話, 那么就必須考慮很多其它方面的事情:

比如睡眠之后又出現了同一IRQ號的中斷又該怎么辦, 等等

其實, 如果自己能把握好,中斷中睡眠也是可以的。但是必須能夠保證這段代碼具有足夠的安全性與可靠性(不破壞調度上下文,能夠被喚醒)。

ref:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM