2011-10-23 78 views
6

可能重複:
Why can't you sleep while holding spinlock?爲什麼在持有螺旋鎖時不允許「睡覺」?

據我所知,自旋鎖應該在短時間的使用,並且在代碼中是唯一的選擇,如中斷處理程序在那裏睡覺(搶佔)是不允許。

但是,我不知道爲什麼有這樣一個「規則」,在舉行螺旋鎖時應該沒有睡覺。我知道這不是一個推薦的做法(因爲它在表演中是有害的),但我沒有看到爲什麼睡眠不應該被允許進入自旋鎖。

,而你獲得一種信號你不能讓一個自旋鎖,因爲你可能不得不睡在等待信號,並在持有自旋鎖(從「Linux內核開發」由羅伯特·愛)你無法入睡。

我能看到的唯一原因是爲了便攜性的原因,因爲在單處理器,自旋鎖作爲禁止中斷,並通過禁用中斷來實現,睡眠當然是不允許的(但睡眠不會打破在SMP系統的代碼) 。

但我想知道我的推理是否正確或者是否有其他原因。

+1

你說這個規則是什麼? –

+0

我剛剛在我的問題出現時添加了一個塊引用。 – SHH

+0

這看起來像我特定於Linux內核開發的東西。如果你正在談論Linux內核開發,那麼請相應地標記問題。 –

回答

13

有幾個原因,至少在Linux中,在自旋鎖睡覺是不允許的:

  1. 如果線程A睡在一個自旋鎖,而線程B,然後試圖獲取同一個自旋鎖,單處理器系統會陷入僵局。線程B永遠不會進入休眠狀態(因爲自旋鎖沒有A完成時喚醒B的必要等待列表),並且線程A永遠不會有機會喚醒。
  2. 自旋鎖被用於信號量正是因爲它們更有效率 - 提供了你不會長期抗衡。允許睡覺意味着你將有很長的爭用時間,消除使用自旋鎖的所有好處。在這種情況下,您的系統會更快地使用信號量。
  3. 自旋鎖通常用於與中斷處理程序同步,另外由另外禁用中斷。如果你睡覺,這個用例是不可能的(一旦你進入中斷處理程序,你不能切換回線程,讓它喚醒並完成它的spinlock臨界區)。

爲正確的工作使用正確的工具 - 如果您需要睡覺,信號量和互斥量是您的朋友。

+1

實際上信號量不應該用在內核代碼中,除非你有一個非常特殊的用例(如果你正在閱讀stackoverflow上的這個問題,你不需要信號量:)當你需要睡覺時,只需使用互斥鎖來鎖定,當你需要鎖定一個不可睡眠的環境時,它會自旋鎖定。 – Roland

+0

@bdonlan我對第一點有疑問。你說過線程A永遠不會醒來?這是爲什麼?當線程B的時間片結束並且線程的休眠時間結束時,線程A將使用其時間片執行,一旦完成將釋放該鎖。情況不是這樣嗎? –

+2

@SumitTrehan:好點。如果自旋鎖對於調度器本身不是必不可少的,並且線程B不在中斷上下文中並且沒有禁止中斷或搶佔,則最終線程B可以被搶佔。 但是,在linux中,當螺旋鎖被保持(或即將被保持)時,搶佔總是被禁用的。這是因爲如果一個線程在不運行時持有自旋鎖,其他線程浪費大量時間旋轉鎖,並且有可能因中斷處理程序造成死鎖。所以在現實中,線程B不會被搶佔並且會死鎖。 – bdonlan

7
  • 其實,你可以睡眠禁用中斷或一些其他類型的排斥活躍。如果你不這樣做,你正在睡覺的條件可能會因爲中斷而改變狀態,然後你永遠不會醒來。通常不會輸入睡眠代碼而不提供高優先級或某個其他關鍵部分,這些部分將休眠決定與上下文切換之間的執行路徑包含在內。

  • 但是對於螺旋鎖,睡眠是一個災難,因爲鎖定保持不變。其他線程在它們打到時會旋轉,並且在你從睡眠中醒來之前它們不會停止旋轉。與自旋鎖最壞情況下預計的少量旋轉相比,這可能是永恆的,因爲自旋鎖只是爲了同步訪問內存位置,它們不應該與上下文切換機制相互作用。

    (對於這個問題,所有其他線程可能最終擊中了自旋鎖,然後你會楔形整個系統的每核心每線程。)

+0

你能否詳細說一下第一點? – SHH

+0

第一點是錯誤的。您無法中斷禁用睡眠;要處理所提到的種族,只需按照正確的順序進行操作:將狀態設置爲TASK_INTERRUPTIBLE,檢查條件,然後調用schedule()或者只使用wait_event()來處理這個問題。 – Roland

+0

@Roland,我只是指內核進程代碼的角度,所以你可以調用schedule()或sleep()入口點。內核的睡眠機制將重新啓用中斷。所有常見的內核現在都是SMP,所以禁用中斷的功能已經不再那麼成功,但可以肯定的是,它不會執行任何硬件_wait-for-interrupt_指令,而無需重新啓用中斷。 – DigitalRoss

1

不能當您使用自旋鎖,因爲它是意味着被使用。自旋鎖用於真正有必要保護關鍵區域和共享數據結構。如果您在持有信號的同時獲取一個信號,則可以鎖定訪問鎖(即,它通常是特定大型數據結構的成員)的任何關鍵區域(比如說),同時允許此進程可能進入休眠狀態。如果在這個進程休眠的時候IRQ被引發,並且IRQ處理程序需要訪問仍然被鎖定的關鍵區域,那麼它會被阻塞,這在IRQ中不會發生。很顯然,你可以舉例說明你的自旋鎖不按照它應該的方式使用(假設的自旋鎖連接到nop循環)。但這根本不是在Linux內核中發現的真正的自旋鎖。