PV操作由P操作原語和V操作原語組成(原語是不可中斷的過程),對信號量進行操作,具體定義如下:
P(S):
①將信號量S的值減1,即S=S-1;
②如果S>=0,則該進程繼續執行;否則該進程置為等待狀態,排入等待隊列。
V(S):
①將信號量S的值加1,即S=S+1;
②如果S>0,則該進程繼續執行;否則釋放隊列中第一個等待信號量的進程。
PV操作的意義:我們用信號量及PV操作來實現進程的同步和互斥。PV操作屬於進程的低級通信。
交互的並發進程因為他們共享資源,一個進程運行時,經常會由於自身或外界的原因而被中端,且斷點是不固定的。也就是說進程執行的相對速度不能由進程自己來控制,於是就會導致並發進程在共享資源的時出現與時間有關的錯誤。
臨界區 : 我們把並發進程中與共享變量有關的程序段稱為臨界區。
信號量S : 信號量的值與相應資源的使用情況有關。當它的值大於0時,表示當前可用資源的數量;當它的值小於0時,其絕對值表示等待使用該資源的進程個數。
進程的互斥:是指當有若干個進程都要使用某一共享資源時,任何時刻最多只允許一個進程去使用該資源,其他要使用它的進程必須等待,直到該資源的占用着釋放了該資源。
進程的同步:是指在並發進程之間存在這一種制約關系,一個進程依賴另一個進程的消息,當一個進程沒有得到另一個進程的消息時應等待,直到消息到達才被喚醒。
【例1】生產者-消費者問題
在多道程序環境下,進程同步是一個十分重要又令人感興趣的問題,而生產者-消費者問題是其中一個有代表性的進程同步問題。下面我們給出了各種情況下的生產者-消費者問題,深入地分析和透徹地理解這個例子,對於全面解決操作系統內的同步、互斥問題將有很大幫助。
①一個生產者,一個消費者,公用一個緩沖區。
empty——表示緩沖區是否為空,初值為1。
full——表示緩沖區中是否為滿,初值為0。
生產者進程:
p(empty);
向buffer放產品;
v(full);
消費者進程:
p(full);
從buffer取產品;
v(empty);
②n個生產者,n個消費者,公用n個緩沖區。
在這個問題中,不僅生產者與消費者之間要同步,而且各個生產者之間、各個消費者之間還必須互斥地訪問緩沖區.
empty——表示緩沖區是否為空,初值為n。
full——表示緩沖區中是否為滿,初值為0。
mutex1——生產者之間的互斥信號量,初值為1。
mutex2——消費者之間的互斥信號量,初值為1。
生產者進程:
生產一件產品; P(empty); P(mutex1); 產品送往buffer(in); in=(in+1)mod n; V(mutex1); V(full);
消費者進程;
P(full); P(mutex2); 從buffer(out)中取出產品; out=(out+1)mod n; V(mutex2); V(empty); 消費該產品;
【例2】
公交車司機與售票員的問題:
1:首先,我們在司機進程使用P操作(S1=S1-1=-1),現在是S1的值為-1,我們來查看P操作發現應該 掛起本進程,也就是說司機進程暫時掛起,我們進入到售票員進程。
2:進入售票員進程后,我們先 關車門,然后我們進行V操作(S1=S1+1=0),發現滿足V操作的else,我們首先喚醒司機進程,然后我們繼續執行售票員進程。
3:接着售票,售票后我們執行P操作(S2=S2-1=-1),發現滿足P操作的else,我們暫時將售票員進程掛起。
4:進入到司機進程。
5:啟動車輛,正常行駛,到站停車,執行V操作(S2=S2+1=0),發現S2滿足V操作的else,喚醒售票員進程,同時繼續執行本進程。
6:但是,我們可以發現司機進程已經執行完了,但是等待隊列中還有售票員進程,我們就進入到售票員進程。
7:在售票員進程中,開車門,上下客。整個司機與售票員問題結束。
【例3】
桌上有一空盤,允許存放一只水果。爸爸可向盤中放蘋果,也可向盤中放桔子,兒子專等吃盤中的桔子,女兒專等吃盤中的蘋果。規定當盤空時一次只能放一只水果供吃者取用,請用P、V原語實現爸爸、兒子、女兒三個並發進程的同步。
下面先考慮同步情況即所有“等待”情況:
第一.爸爸要等待盤子為空。
第二.兒子要等待盤中水果是桔子。
第三.女兒要等待盤中水果是蘋果。
接下來來考慮要互斥處理的資源,看起來盤子好像是要作互斥處理的,但由於題目中的爸爸、兒子、女兒均只有一個,並且他們訪問盤子的條件都不一樣,所以他們根本不會同時去訪問盤子,因此盤子也就不用作互斥處理了。分析至些,這個題目已經沒有難度了,下面用PV原語給出答案:
先設置三個信號量,信號量Orange表示盤中有桔子,初值為0。信號量Apple表示盤中有蘋果,初值為0。信號量EmptyDish表示盤子為空,初值為1。三個人的操作流程如下所示:
爸爸進程:
P(EmptyDish) if (rand()%2==0) { 放桔子 V(Orange) } else { 放蘋果 V(Apple) }:
兒子進程:
P(Orange)
取桔子
V(EmptyDish)
女兒進程:
P(Apple)
取蘋果
V(EmptyDish)