2010-04-01 32 views
17

在SMP機器上,我們必須從中斷上下文中使用spin_lock_irqsave而不是spin_lock_irqspin_lock_irqsave vs spin_lock_irq

爲什麼我們要保存標誌(包含IF)?

是否有另一箇中斷程序可以打斷我們?

回答

19

我是內核新手,但是從Robert Love的書「Linux Kernel Development」中收集的內容,如果在代碼開始鎖定之前處理器上已禁用中斷,則在調用spin_unlock_irq時,將釋放鎖錯誤的方式。如果您保存標誌並將其與標誌一起釋放,則函數spin_lock_irqsave將僅使中斷返回到之前的狀態。

例與spin_lock_irqsave

spinlock_t mLock = SPIN_LOCK_UNLOCK; 
unsigned long flags; 

spin_lock_irqsave(&mLock, flags); // save the state, if locked already it is saved in flags 
// Critical section 
spin_unlock_irqrestore(&mLock, flags); // return to the formally state specified in flags 

例與spin_lock_irq(無irqsave):

spinlock_t mLock = SPIN_LOCK_UNLOCK; 
unsigned long flags; 

spin_lock_irq(&mLock); // Does not know if already locked 
// Critical section 
spin_unlock_irq(&mLock); // Could result in an error unlock... 
+2

這個答案不正確。 spin_lock_irq將不會立即禁用中斷,而irqsave變體將在您無法知道當前處於什麼狀態的情況下保存中斷狀態。 – 2013-05-10 21:18:15

+3

@NoahWatkins這是最後一部分的要點。它說*可能導致錯誤解鎖*在評論中。 'spin_lock_irq'用於表明不應該使用IRQ。答案並不正確;只是不太清楚。 – 2014-04-25 20:16:39

+0

@artlessnoise當我們說irq被禁用時,系統中的所有irq都被禁用了嗎?這對我來說不是很清楚。你可以解釋嗎? – 2016-03-28 09:59:54

4

閱讀Why kernel code/thread executing in interrupt context cannot sleep?可鏈接到羅伯特·愛article,我讀了這一點:

一些中斷處理程序(在中已知Linux作爲快速中斷處理程序)運行 ,禁用本地 處理器上的所有中斷。這樣做到 確保中斷處理程序運行 不中斷,儘可能快地 。更甚的是,所有中斷 處理程序在其所有 處理器上都禁用其當前 中斷行的情況下運行。這確保了同一個 中斷線的兩個 中斷處理程序不會同時運行 。它還會阻止驅動程序編寫者的設備 必須處理遞歸中斷,這會使編程複雜化。

+4

這不回答問題。 – JagsVG 2016-01-06 07:12:07

28

spin_lock_irqsave基本上是用來走的是自旋鎖之前保存中斷狀態,這是因爲自旋鎖禁止中斷,當鎖被採取中斷上下文,並重新啓用它,而解鎖時。中斷狀態被保存,以便它應該再次恢復中斷。

實施例:

  1. 比方說被收購
  2. spin_lock_irq之前自旋鎖將禁用中斷x和搭鎖
  3. spin_unlock_irq將使中斷X中斷X被禁用。

因此,在釋放鎖定之後的第3步中,我們將啓用在獲取鎖之前先前被禁用的中斷x。

所以只有當你確定中斷沒有被禁止時,你應該spin_lock_irq否則你應該總是使用spin_lock_irqsave

2

需要spin_lock_irqsave除了spin_lock_irq是非常相似的原因local_irq_save(flags)是需要的除了local_irq_disable。下面是羅伯特·洛夫在Linux Kernel Development第二版中對這個要求的一個很好的解釋。

如果在調用之前中斷已被禁用,則local_irq_disable()例程是危險的。對 local_irq_enable()的相應調用無條件地啓用中斷,儘管事實上它們是關閉的。相反,需要一個機制 將中斷恢復到以前的狀態。這是一個常見的問題 ,因爲內核中給定的代碼路徑可以通過和 而不啓用中斷,具體取決於調用鏈。例如, 想象前面的代碼片段是更大函數的一部分。 想象一下,這個函數被另外兩個函數調用,一個是禁止中斷,另一個不是。由於內核的規模和複雜程度越來越高,以致於知道導致功能的所有代碼路徑,因此在禁用中斷系統之前保存中斷系統的狀態要安全得多。然後,當你準備 重新啓用中斷,您只需將它們恢復到原來的狀態:

unsigned long flags; 

local_irq_save(flags); /* interrupts are now disabled */ /* ... */ 
local_irq_restore(flags); /* interrupts are restored to their previous 
state */ 

注意,這些方法在一部分宏至少實現的,所以 標誌參數(它必須被定義爲一個無符號長整數)是 似乎按值傳遞。此參數包含 體系結構特定的數據,其中包含中斷 系統的狀態。由於至少有一個支持的體系結構將堆棧信息合併到值(ahem,SPARC)中,因此不能將 標誌傳遞給另一個函數(具體而言,它必須保留在相同的堆棧 幀中)。出於這個原因,保存的呼叫和恢復中斷的呼叫必須發生在相同的功能中。

所有以前的函數都可以從中斷和 進程上下文中調用。

相關問題