請問這一段代碼的加鎖原理是什麼?

這是關於關於操作系統的線程鎖實現的一段代碼,摘自wisc大學那本經典的操作系統教材。

我想不通這個guard變數的作用是什麼,既然線程獲取到了鎖以後,把flag置位了,那TestAndSet函數里第一個參數為什麼不是flag的地址?

還有這一段也看不懂

我是操作系統初學者,第一次啃這本書,希望有懂得大神能幫我解釋一下,不勝感激!

傳送門:http://pages.cs.wisc.edu/~remzi/OSTEP/threads-locks.pdf


加鎖的代碼同樣需要保護。所以必須有一個guard,來保證加鎖過程不被打斷。

這段代碼中,TestAndSet 是CPU提供的原子操作指令。它是各種鎖演算法的核心。

除了原子操作指令,其它指令都可能在執行過程中出現「執行權切換」。多條指令構成的一段程序就更不用說了。

如果guard被置1,就說明flag正在被其它進程/線程修改,那麼當前代碼就不能碰flag了;否則,說明flag可以安全修改。

如果先檢查guard、然後再置1,那麼就可能在檢查guard之後、對它置1之前出現控制權切換。這就可能導致兩個進程同時拿到鎖。所以這裡必須用TestAndSet指令——正如前面說過,加鎖代碼同樣需要保護,TestAndSet指令給guard加鎖時,也是如此。這個保護由硬體自動完成(也可能是TestAndSet函數內部用內存屏障指令手動鎖)。

換句話說,硬體保護TestAndSet指令執行時guard的安全;guard又保護lock函數執行時,flag的安全;flag最終被程序員用來保護某種共享資源的安全——整個傳遞鏈條,任何一環出問題都不行。


TestAndSet 也是一個原子操作,guard 值如果為 1,就表示別的線程正在執行 lock 操作,比如正在設置 flag 或 enqueue,那麼 while 語句就會構成一個 SpinLock,直到其他線程把 guard 重新置 0,這個線程才有機會通過 TestAndSet 將 guard 設為 1,另外原子操作的具體功能和實現可能不一致,在這裡應該是設置完返回原始值。


這個是對spinlock的改造,加入阻塞隊列,而不是忙等。如果只有一個flag,那麼阻塞的時候如何修改flag呢。必須在阻塞之前釋放spin鎖,那麼釋放鎖之後再阻塞,隊列操作就會被打斷,因此需要一個guard。guard就是spinlock,用來保護flag,阻塞隊列的,而flag用來指示是否需要掛起。如果沒有阻塞隊列維護,guard跟flag就可以合併,就是spin鎖了。

其實實際系統spinlock還要關中斷或者禁止當前cpu切換線程。否則cpu A線程1獲取spinlock後,發生線程切換,那麼其他cpu再來獲取spinlock時都會忙等,等到cpu A線程1繼續運行才行,這樣就太浪費cpu了。spinlock一般用於cpu間同步,獲取鎖之後應該儘快釋放。


感覺這個spin lock的flag和q在可見性上有BUG呀,難道上層保證了這個每個lock_t都只在一個CPU上運行?


非計算機科班出身,沒深入學過操作系統。不過就這個代碼本身來說,guard只是用來保證lock和unlock函數本身的原子性。

如果只看lock函數,按你的方法,用flag代替lock確實可以保證兩個lock調用的互斥。但是如何保證兩個unlock的互斥,或者lock和unlock間的互斥呢?用guard應該是一種簡單明了的辦法。

其實這裡可以把lock和unlock換成任何功能的函數,guard都可以用來保證原子性。

所學有限,如有差錯,還請海涵


推薦閱讀:

有限條件下怎樣舉辦一場知名度高、吸引大牛的編程比賽?
為什麼有軟體可以繞過UAC?
第一次使用 Linux 的純小白應該了解哪些東西?
MIUI有哪些細節讓你覺得驚喜?
禁用虛擬內存,操作系統還能實現虛擬存儲器這種機制嗎?

TAG:操作系統 | 編程 | C編程語言 | 線程 |