鎖的實現機制

在多線程編程中,爲了保證數據操作的一致性,操作系統引入了鎖機制,用於保證臨界區代碼的安全。通過鎖機制,能夠保證在多核多線程環境中,在某一個時間點上,只能有一個線程進入臨界區代碼,從而保證臨界區中操作數據的一致性。
所謂的鎖,說白了就是內存中的一個整型數,擁有兩種狀態:空閒狀態和上鎖狀態。加鎖時,判斷鎖是否空閒,如果空閒,修改爲上鎖狀態,返回成功;如果已經上鎖,則返回失敗。解鎖時,則把鎖狀態修改爲空閒狀態。
於是加鎖這個過程可以分爲這幾步操作

(1)  read lock;

(2)  判斷lock狀態;

(3) 如果已經加鎖,失敗返回;

(4) 把鎖狀態設置爲上鎖;

(5) 返回成功。

如何確保加鎖本身這個操作的原子性

舉個例子,在多核環境中,兩個核上的代碼同時申請一個鎖,兩個核同時取出鎖變量,同時判斷說這個鎖是空閒狀態,然後有同時修改爲上鎖狀態,同時返回成功。。。兩個核同時獲取到了鎖,這種情況可能嗎?廢話,當然是不可能,可能的話,我們使用鎖還有啥意義。

那麼什麼情況能夠導致兩個線程同時獲取到鎖呢?假設開始時沒有任何線程獲得鎖。
1、中斷:假設線程A執行完第一步,發生中斷,中斷返回後,OS調度線程B,線程B也來加鎖並且加鎖成功,這時OS調度線程A執行,線程從第二步開始執行,也加鎖成功。
2、多核:當然了,想想上面舉的例子,描述的就是兩個核同時獲取到鎖的情況。
既然明白鎖失敗的原因,解決手段就很明確了。

先考慮單核場景:
1、既然只有中斷才能把上鎖過程打斷,造成多線程操作失敗。我先關中斷不就得了,在加鎖操作完成後再開中斷。
2、上面這個手段太笨重了,能不能硬件做一種加鎖的原子操作呢?能,大名鼎鼎的**“test and set**”指令就是做這個事情的。

通過上面的手段,單核環境下,鎖的實現問題得到了圓滿的解決。
那麼多核環境呢?簡單嘛,還是**“test and set**”不就得了,這是一條指令,原子的,不會有問題的。真的嗎,單獨一條指令能夠保證該指令在單個核上執行過程中不會被中斷打斷,但是兩個核同時執行這個指令呢?。。。我再想想,硬件執行時還是得從內存中讀取lock,判斷並設置狀態到內存,貌似這個過程也不是那麼原子嘛。
對,多個核執行確實會存在這個問題。怎麼辦呢?首先我們得明白這個地方的關鍵點,關鍵點是兩個核會並行操作內存而且從操作內存這個調度來看“test and set”不是原子的,需要先讀內存然後再寫內存,如果我們保證這個內存操作是原子的,就能保證鎖的正確性了。確實,硬件提供了鎖內存總線的機制,我們在鎖內存總線的狀態下執行test and set操作,就能保證同時只有一個核來test and set,從而避免了多核下發生的問題。
總結一下,在硬件層面,CPU提供了原子操作、關中斷、鎖內存總線的機制;OS基於這幾個CPU硬件機制,就能夠實現鎖;再基於鎖,就能夠實現各種各樣的同步機制(信號量、消息、Barrier等等等等)。所以要想理解OS的各種同步手段,首先需要理解本文介紹的內容,這是最原點的機制,所有的OS上層同步手段都基於此。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章