PV操作原語和訊號量sem是電腦作業系統進程和線程同步的核心手段,雖然說起來只有句話,但有幾個點非常容易引起模糊。先把PV操作的說明如下:
P操作原語:
1. sem 減1
2. 若sem 大於等於0,線程繼續執行.
3. 若sem < 0 ,線程進入阻塞隊列.
V 操作原語:
1. sem加1
2. 若sem 大於 0, 線程繼續執行
3. 若sem 小於等於0,喚醒阻塞隊例的線程
問題1.原語概念
教科書上一般定義是不允許發生中斷,一段必須連續執行的指令。在Dijkstra發明原語概念的時代,CPU應該都是單核的,多線程是單個CPU分時間片執行,所有只要是保證是” 連續執行”,就不會發生執行一個線程P操作的sem減1後,另一個線程V操作sem 加1。這意味著可以在現有的程式層面就可以實現多線程的同步而不需要別的指令或硬體。但在現在的多核時代,完全有可能在一個核上執行P操作sem減1後,另一個核上執行V操作sem加1,修改相同的地址空間,從而破壞sem的狀態. 這方面.NET和Java都提供了volatile關鍵字,估計是添加的新的指令來保證,不過還好,這是作業系統操心的事情,作業系統保證原語概念的完整性。
問題2. 訊號量
訊號量sem在PV操作裡具有三元性質.
讓我們來看一個具體的執行個體: 假設現在的執行情境是有三個線程A,B,C進入一個訊號量為1的臨界資源,
1. 當線程A進入時,執行P操作, sem=0,線程A繼續執行.
2. 當線程B進入時,線程A假設仍在佔用臨界資源, B執行P操作,sem = -1,B進入等待隊列。
3. 當線程C進入時,線程A假設仍在佔用臨界資源, C執行P操作,sem = -2,C進入等待隊列
通過以上的步驟可以發現:
1) 當sem > 0 時,表示可用的臨界資源的數量
2) 當sem = 0時,表示無可用的資源,也沒有阻塞的線程
3) 當sem <0 時,sem的絕對值表示阻塞隊列的線程數
問題3.對V操作2,3步驟的解釋
V操作的第2步,sem > 0 ,線程繼續執行.有些人這個地方不解,sem > 0不是代表有資源可用嗎?為什麼不是喚醒其它的線程。其實正是sem >0 ,sem當前的意義是有資源可用,而沒有阻塞的線程,所有根本不需要喚醒.
V操作的第3步,sem 小於等於0, 喚醒阻塞隊例的線程. sem < 0很多人直覺認識都是沒資源可用了,幹嗎還喚醒阻塞隊列的線程(包括我)。通過問題2的分析,在這個時候sem代表的是阻塞隊列的線程數 ,所以需要喚醒. 讓我們接著問題2的步驟.
4) 線程A資源使用完,執行V操作,sem = -1, 喚醒阻塞隊列的線程(假設FIFO),B線程進入臨界資源開始執行,注意,B不會再執行P操作,因為進入阻塞隊列前已執行.P,V操作必須成對執行。
5) B使用完資源後,執行V操作,sem = 0; 喚醒阻塞隊列的線程,C線程進入臨界資源開始執行,C也不會再執行P操作
6) C使用完資源,執行V操作, sem = 1
PV操作由P操作原語和V操作原語組成(原語是不可中斷的過程)。對訊號量進行操作,具體定義如下:
P(S):
①將訊號量S的值減1,即S = S - 1;
②如果s >= 0,則該進程繼續執行;否則該進程置為等待狀態,排入等待隊列。
V(S):
①將訊號量S的值加1,即S = S + 1;
②如果S > 0,則該進程繼續執行;否則釋放隊列中第一個等待訊號量的進程。
PV操作的意義:我們用訊號量及PV操作來實現進程的同步和互斥。PV操作屬於進程的低級通訊。
訊號量(semaphore)的資料結構為一個值和一個指標,指標指向等待該訊號量的下一個進程。訊號量的值與相應資源的使用方式有關。當它的值大於0時,表示當前可用資源的數量;當它的值小於0時,其絕對值表示等待使用該資源的進程個數。注意,訊號量的值僅能由PV操作來改變。
一般來說,訊號量S >= 0時,S表示可用資源的數量。執行一次P操作意味著請求分配一個單位資源,因此S的值減1;當S<0時,表示已經沒有可用資源,要求者必須等待別的進程釋放該類資源,它才能運行下去。而執行一個V操作意味著釋放一個單位資源,因此S的值加1;若S <= 0,表示有某些進程正在等待該資源,因此要喚醒一個等待狀態的進程,使之運行下去。也就是說,有一個未被佔用的資源就可以讓一個阻塞的進程執行,
而不是S為正是才可以執行。