1. 程式人生 > >Linux核心之禁止中斷和禁止核心搶佔

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.自旋鎖關中斷後,為什麼要再禁止搶佔?

在百度上找了很久答案,貼一個靠譜的回答

假設有這麼個情況:

  1. CPU-1在程序A的上下文呼叫了spin_lock_irqsave;
  2. CPU-2呼叫wake_up_process喚醒了CPU-1上的程序B,由於程序B的優先順序高於程序A,程序A的TIF_NEED_RESCHED標記被設定。(CPU-2還會用IPI通知CPU-1進行resched,但是CPU-1禁用了中斷而不會響應);
  3. CPU-1呼叫了某某函式,這個函式包含了preempt_disable和preempt_enable(沒有規定關中斷的情況下不能呼叫這樣的函式吧~);
  4. 那麼,如果spin_lock_irqsave沒有preempt_disable,第3步中的preempt_enable將觸發preempt_check_resched,從而讓程序B搶佔掉程序A。
總之就是隻有關了搶佔,才能保證在臨界區成對出現的preempt_disable()/preempt_enable()(preempt_enable()也是一個潛在的主動排程的測試點)不會造成傷害。不然這種程式碼就不能放在臨界區中了。