操作系統概念學習筆記 12
進程同步(二)
管程
基本的、高級的同步構造,即管程(monitor)類型。
使用:
管程類型提供了一組由程序員定義的、在管程內互斥的操作。管程類型的表示包括一組變量的聲明(這些變量的值定義了一個類型實例的狀態)和對這些變量操作的子程序和函數的實現。管程的類型表示不能直接爲各個進程所使用。因此,在管程內定義的子程序只能訪問位於管程內那些局部聲明的變量和形式參數。類似的,管程的局部變量能被局部子程序訪問。
管程結構確保一次只有一個進程能在管程內活動。不需要顯示的編寫同步代碼。而對於特定同步方案,需要額外的同步機制,這些由條件(condition)結構來提供。
condition x,y;
x.wait();
x.signal();
管程的語法:
monitor monitor name{
//shared variable declarations
procedure P1(…){
…
}
procedure P2(…){
…
}
…
procedure Pn(…){
…
}
initialization code(…){
…
}
}
哲學家進餐問題的管程解法
這個解決方案要求哲學家在兩隻筷子都可以使用時纔會拿起筷子。
爲此,引入如下數據結構:
enum {THINKING, HUNGRY, EATTING} state[5];
加入條件,哲學家i只有在其兩個鄰居不再進餐時才能將變量state[i]設置爲eating:
(state[(i+4)%5]!=eating)和(state[i+1]%5!=eating)
哲學家i必須按以下順序來調用操作
dp.pickup(i)
...
eat
...
dp.putdown(i)
基於信號量的管程實現
基於信號量的哲學家進餐問題的管程解法:每個管程都有一個信號量mutex(初始化爲1),進程在進入管程之前,必須執行wait(mutex),在離開管程後必須執行signal(mutex)。
monitor dp{
enum{THINKING,HUNGRY,EATING}state[5];
condition self[5];
void pickup(int i){
state[i]=HUNGRY;
test(i);
if(state[i]!=EATING)
self[i].wait();
}
void putdown(int i){
state[i]=THINKING;
test((i+4)%5);
test((i+1)%5);
}
void test(int i){
if((state[(i+4)%5]!=EATING)&&(state[i]==HUNGRY)&&(state[(i+1)%5]!=EATING)){
state[i]=EATING;
self[i].signal();
}
}
initialization_code(){
for(int i=0;i<5;i++)
state[i]=THINKING;
}
}
條件變量的實現:對於每個條件變量x,引入信號量x_sem和整數變量x_count,兩者均初始化爲0。由於信號進程必須等待,引入另一個信號量next以供信號進程掛起自己,next_count以對掛起在next上的進程進行計數。
x.wait()的實現:
x_count++;
if(next_count > 0)
signal(next);
else
signal(mutex);
wait(x_sem);
x_count--;
x.signal()的實現:
if(x_count>0){
next_count++;
signal(x_sem);
wait(next);
next_count--;
}
管程內的進程重啓
等待最長的進程先重新運行。也可以使用條件等待構造。
x.wait(c);其中c表示優先值(priority number),會與懸掛進程的名稱一起存儲。
使用管程來管理資源時,爲確保系統的正確,有兩個條件是必須檢查的:
第一,用戶進程必須總是按正確順序來對管程進行調用;
第二,必須確保一個不合作的進程不能簡單地忽略由管程所提供的互斥關口,以及在不遵守協議的情況下直接訪問共享資源。