今天,我們來講解一些關於加鎖解鎖的知識。Unix操作系統加鎖和解鎖的基本思想是,當某個進程進入臨界區,它將持有一個某種類型的鎖(UNIX裡一般來說是semaphore,Linux裡一般是信號量和原子量或者spinlock)。當其他進程在該進程沒有釋放該鎖時試圖進入臨界區(加鎖),它將會被設置成睡眠狀態,然後被置入等待該鎖的進程隊列(某個優先級的)。
當Unix操作系統該鎖被釋放時,也就是解鎖事件發生時,內核將從等待該鎖的進程優先級隊列中尋找一個進程並將其置為就緒態,等待調度(schedule)。
在system v中,等待某一事件被稱為sleep(sleep on an event),因此下文將統一使用睡眠(sleep)。Unix操作系統等待某事件也可以成為等待某個鎖。(注:本文中的sleep與sleep()系統調用不同)
Unix操作系統的實現將一組事件映射到一組內核虛擬地址(鎖);而且事件不區別對待到底有多少進程在等待。這就意味著兩個不規則的事情:
一、當某個事件發生時,Unix操作系統等待該事件的一組進程均被喚醒(而不是僅僅喚醒一個進程),並且狀態均被設置成就緒(ready-to-run)。
這時候由內核選擇(schedule)一個進程來執行,由於system v內核不是可搶占的(Linux內核可搶占),因此其他的進程將一直在就緒狀態等待調度,或者再次進入睡眠(因為該鎖有可能被執行進程持有,而執行進程因為等待其他事件的發生而睡眠),或者等其他進程在用戶態被搶占。
二、多個事件映射到同一個地址(鎖)。假設事件e1和e2都映射到同一個地址(鎖)addr,有一組進程在等待e1,一組進程在等待e2,它們等待的事件不同,但是Unix操作系統對應的鎖相同。
假如e2發生了,所有等待e2的進程都被喚醒進入就緒狀態,而由於e1沒有發生,鎖addr沒有被釋放,所有被喚醒的進程又回到睡眠狀態。
Unix操作系統貌似一個事件對應一個地址會提高效率,但實際上由於system v是非搶占式內核,而且這種多對一映射非常少,再加上運行態進程很快就會釋放資源(在其他進程被調度之前),因此這種映射不會導致性能的顯著降低。