void spin_lock(spinlock_t *lock);
void spin_lock_irq(spinlock_t *lock);
void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);
1、spin_lock與spin_lock_irq差別
在Linux核心中何時使用spin_lock,何時使用spin_lock_irqsave很容易混淆。首先看一下代碼是如何實作的。
spin_lock的調用關系
spin_lock
|
+ -----> raw_spin_lock
|
+------> _raw_spin_lock
|
+--------> __raw_spin_lock
1. static inline void
2. {
3. preempt_disable();
4. spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
5. LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
6. }
spin_lock_irq的調用關系
spin_lock_irq
|
+-------> raw_spin_lock_irq
|
+---------> _raw_spin_lock_irq
|
+------------> __raw_spin_lock_irq
1. static inline void
2. {
3. local_irq_disable();
4. preempt_disable();
5. spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
6. LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
7. }
可以看出來他們兩者隻有一個差别:是否調用local_irq_disable()函數, 即是否禁止本地中斷。
在任何情況下使用spin_lock_irq都是安全的。因為它既禁止本地中斷,又禁止核心搶占。
spin_lock比spin_lock_irq速度快,但是它并不是任何情況下都是安全的。
舉個例子:程序A中調用了spin_lock(&lock)然後進入臨界區,此時來了一個中斷(interrupt),
該中斷也運作在和程序A相同的CPU上,并且在該中斷處理程式中恰巧也會spin_lock(&lock)
試圖擷取同一個鎖。由于是在同一個CPU上被中斷,程序A會被設定為TASK_INTERRUPT狀态,
中斷處理程式無法獲得鎖,會不停的忙等,由于程序A被設定為中斷狀态,schedule()程序排程就
無法再排程程序A運作,這樣就導緻了死鎖!
但是如果該中斷處理程式運作在不同的CPU上就不會觸發死鎖。 因為在不同的CPU上出現中斷不會導緻
程序A的狀态被設為TASK_INTERRUPT,隻是換出。當中斷處理程式忙等被換出後,程序A還是有機會
獲得CPU,執行并退出臨界區。
是以在使用spin_lock時要明确知道該鎖不會在中斷處理程式中使用。
2、spin_lock_irq與spin_lock_irqsave差別
spin_lock_irqsave在進入臨界區前,儲存目前中斷寄存器flag狀态,關中斷,進入臨界區,在退出臨界區時,把儲存的中斷狀态寫回到中斷寄存器。
spin_lock_irq在進入臨界區前不儲存中斷狀态,關中斷,進入臨界區,在退出臨界區時,開中斷。
spin_lock_irqsave鎖傳回時,中斷狀态不會被改變,調用spin_lock_irqsave前是開中斷傳回就開中斷。