Linux核心之禁止中斷和禁止核心搶佔
禁止中斷指的是Linux核心停工了一組介面用於操作機器上的中斷狀態。這些介面為我們提供了能夠禁止當前處理器的中斷系統,或者遮蔽掉整個機器的一條中斷線的能力。通過禁止中斷,可以確保某個中斷處理程式不會搶佔當前的程式碼。控制中斷系統在Linux的實現有很多,以local_irq_disable()和 local_irq_enable()函式
//我看不懂,反正就是依賴於體系結構,通過彙編呼叫實現 static inline void local_irq_enable(void) { unsigned long tmpreg; __asm__ __volatile__( "mvfc %0, psw; \n\t" "or3 %0, %0, #0x0040; \n\t" "mvtc %0, psw; \n\t" : "=&r" (tmpreg) : : "cbit", "memory"); } static inline void local_irq_disable(void) { unsigned long tmpreg0, tmpreg1; __asm__ __volatile__( "ld24 %0, #0 ; Use 32-bit insn. \n\t" "mvfc %1, psw ; No interrupt can be accepted here. \n\t" "mvtc %0, psw \n\t" "and3 %0, %1, #0xffbf \n\t" "mvtc %0, psw \n\t" : "=&r" (tmpreg0), "=&r" (tmpreg1) : : "cbit", "memory"); }
禁止核心搶佔就比較簡單了,就是防止當前程序不會突然被另一個程序搶佔。在Linux的實現就是preempt_disable()和preempt_enable()函式
#define preempt_disable() \ do { \ //增加preempt_count inc_preempt_count(); \ //保證先加了preempt_count才進行以後的操作 barrier(); \ } while (0) #define preempt_enable() \ do { \ preempt_enable_no_resched(); \ barrier(); \ //檢查當前程序是否可搶佔 preempt_check_resched(); \ } while (0)
不管是禁止中斷還是禁止核心搶佔,都是為了提供核心同步,但是他們都沒有提供任何保護機制來防止其它處理器的併發訪問。Linux支援多處理器,因此,核心程式碼一般都需要獲取某種鎖,防止來自其他處理器對共享資料的併發訪問,而禁止中斷提供保護機制,這是防止來自其他中斷處理程式的併發訪問。
前面說的都是概念,現在我們來討論幾個問題
1.在單處理器條件下,為什麼禁止中斷就可以禁止核心搶佔?
這個問題困擾了我很久,我也只是說說個人的理解,先來回顧一下核心搶佔發生在哪些時候:
1. 在中斷返回核心空間的時候,這個沒什麼好說的,跟中斷密切相關,沒了中斷就不會發生
2. 核心顯式呼叫schedule()(可搶佔或阻塞)
我們先搞清楚一件事,就是我們說禁止中斷可以禁止核心搶佔只是說禁止任何意外的搶佔,如果程序自己要呼叫schedule函式,那誰也攔不住,事實上呼叫schedule這個函式本來就要禁止中斷,所以剩下的就是考慮建立或者喚醒一個更高優先順序的程序,或者呼叫訊號量、完成量,所有的這些情況都要通過try_to_wake_up函式喚醒另一個程序,但是這個函式真正乾的事只是設定了一下need_resched這個函式,並沒有真的呼叫schedule函式,呼叫是在系統呼叫返回使用者空間的時候進行的,所以跟核心搶佔也沒啥關係,具體可以看這篇部落格http://blog.csdn.net/wishfly/article/details/264663,所以從這些方面來說,禁止中斷是可以禁止核心搶佔的
2.自旋鎖關中斷後,為什麼要再禁止搶佔?
在百度上找了很久答案,貼一個靠譜的回答
假設有這麼個情況:
- CPU-1在程序A的上下文呼叫了spin_lock_irqsave;
- CPU-2呼叫wake_up_process喚醒了CPU-1上的程序B,由於程序B的優先順序高於程序A,程序A的TIF_NEED_RESCHED標記被設定。(CPU-2還會用IPI通知CPU-1進行resched,但是CPU-1禁用了中斷而不會響應);
- CPU-1呼叫了某某函式,這個函式包含了preempt_disable和preempt_enable(沒有規定關中斷的情況下不能呼叫這樣的函式吧~);
- 那麼,如果spin_lock_irqsave沒有preempt_disable,第3步中的preempt_enable將觸發preempt_check_resched,從而讓程序B搶佔掉程序A。