點這裡圖片更清晰
自旋鎖是一個很神奇的東西,一個介於高效和低效之間的一個 『薛定諤』??
的互斥機制。
自旋鎖的效率和它的應用場景有很大關係,在實際生產過程中,我們能在很多地方看見它的身影。
比如Linux kernal有挺多地方用到spinlock、 Nginx也有用到spinlock, 但很多時候自旋鎖在很多場景下並不能很好的發揮出它的高效優勢。
究竟什麼時候我們應該使用SpinLock?
首先,要注意的是自旋鎖只適用於多核心狀態下(這個多核心指的是當前進程可用的核心數 > 1), 比如說你是一個8核Mac,但是你在一個限制1核的Docker環境中用SpinLock,卵用也沒有!!!
本質上,SpinLock之所以有效就是假定,當前存在另外一個CPU核心正在使用你所需要的資源。CPU只有一個你等也是白等。 (就好像一個癡漢暗戀一個人一樣,劃掉??)
其次,SpinLock之所以在一些場景下很高效是因為旋等消耗的時鐘週期遠小於上下文交換產生的時間。
我們來回顧一下Mutex 睡眠等待的過程。首先是嘗試上一次鎖,如果不行的話就通過調度演算法找到一個優先順序更高的Thread,然後纔是保存寄存器,寫回被修改的數據,然後纔是交換上下文。
可以看到這個代價是十分大的,而且交換上下文的代價是要??2的。一般來說,這個代價,在幾千~幾十萬時鐘週期。回顧一下一個4GHz的8代處理器,一個時間週期=0.25ns。交換一次的代價還是挺可觀的。
所以我們使用SpinLock的時候就需要保證我們的臨界區代碼,能夠在這個時鐘週期之類完成所有任務。
所以一般spinLock等待的代碼不會太長,一般幾行(具體需要看晶元和編譯環境),更不可能是I/O等待型的任務。(在XV6中,關於文件系統的操作都單獨使用基於SpinLock的SleepLock)
然後,其實SpinLock更適合系統態,不太適合用戶態。因為你用戶態沒法知道有沒有另外一個CPU在處理你所需要的資源。而且SpinLock並不適合多線程搶佔一個資源的場景,比如說開了60個線程搶佔一個一個內存資源,這個競爭、等待的代價就是超級大的一個數。
所以,其實自旋鎖的優勢、劣勢都很明顯,怎麼來更好的用好它就是程序員??????的事情啦。
SpinLock in XV6
XV6其實是一個很Unix的教學操作系統,通過對XV6代碼的閱讀,我們其實能夠以更少代價來瞭解Unix是怎麼做的。
SpinLock在XV6中定義在和兩個文件中,實際上代碼量不過100行,是很好的分析案例。