作業系統核心

核心自旋鎖(spinlock)

核心裡的兩個 CPU 核心可能同時試圖碰觸同一個資料結構,沒有協調它們就會把它弄壞。一把鎖讓其中一個等待,另一個幹活。自旋鎖是核心裡最簡單的一種鎖:當一個核心因另一個核心持有而拿不到鎖時,它不去睡眠——它就自旋,待在一個緊密的迴圈裡不斷重新檢查那把鎖,直到它變空閒,然後一把抓住。這就是忙碌等待:燒掉 CPU 週期,除了等待之外什麼有用的事都不做。

自旋聽起來浪費,對於長時間的等待確實如此——但它對非常短的臨界區間恰恰正確,因為它避開了把工作放去睡眠、稍後再喚醒的負擔。如果鎖只會被持有寥寥幾條指令,自旋幾個週期遠比一次上下文切換便宜。不過,自旋鎖存在更深的理由是:它是中斷上下文裡唯一可用的鎖,那裡根本不能睡眠。一個需要保護共享資料的上半部中斷處理常式或 softirq 別無選擇:它必須用自旋鎖,因為會睡眠的替代品(互斥鎖)在沒有工作可放去睡的地方是被禁止的。

以下是你絕不可違反的規則,也是關於自旋鎖最重要的記憶點:持有自旋鎖時你不可睡眠。想像你持有一把自旋鎖,然後做了某件會阻塞的事——持有者現在睡著了、不在跑,而另一個核心永遠自旋著等待一把永遠不會被釋放的鎖,因為持有者無法醒來釋放它。這是一個能凍結機器的死結。所以自旋鎖下的程式碼必須短、不可呼叫任何可能睡眠的東西(不可有會睡眠的記憶體配置、不可有可能出錯而阻塞的 copy_from_user、不可有互斥鎖),而在單處理器上它通常還會停用搶占(有時還停用中斷),使持有者不致在區段中途被排程走。當工作可能需要睡眠時,你改用互斥鎖,而非自旋鎖。

spin_lock(&lock); list_add(&node, &shared_list); spin_unlock(&lock); // 微小,內部不睡眠。絕不可:spin_lock(&lock); mutex_lock(&m); /* 可能睡眠 -> 死結 */;或在自旋鎖下做 kmalloc(n, GFP_KERNEL)(可能睡眠)。

自旋鎖以忙碌等待守護一個微小的臨界區間。持有它時,你絕不可呼叫任何會睡眠的東西。

首要規則:持有自旋鎖時絕不睡眠——持有者會阻塞,而其他核心永遠自旋等待,使機器死結。只把自旋鎖用於非常短、不睡眠的臨界區間(且它是中斷上下文裡唯一允許的鎖);若工作會阻塞,改用會睡眠的互斥鎖。

又称
spinlockbusy-wait lockspin lock自旋鎖