作業系統5————程序同步的經典問題:司機售票員&問題生產者消費者問題&哲學家進餐問題&讀者寫者問題
作業系統5————程序同步的經典問題:司機售票員&問題生產者消費者問題&哲學家進餐問題&讀者寫者問題
一. 目錄
二. 訊號量的簡單應用
1. 使用訊號量實現互斥
思路:設mutex為互斥訊號量,其初值為1,取值範圍為(-1,0,1)。當mutex=1時,表示兩個程序皆未進入需要互斥的臨界區,當mutex=0時,表示有一個程序進入臨界區。另一個必須等待,掛入阻塞佇列。當muntex=-1時,表示有一個程序因等待而阻塞在訊號量佇列,需要被當前已在臨界區執行的程序推出是喚醒
程式碼描述
semaphore mutex = 1; pA(){ while(1){ wait(mutex); 臨界區 signal(mutex); 剩餘區 } } PB(){ while(1){ wait(mutext); 臨界區; signal(mutex); 剩餘區 } }
2. 使用訊號量實現前驅關係
問題: 現在有程序P1和P2,P1有語句S1,P2有語句S2,我們希望執行完S1後在執行S2。 思路: 我們只需要在程序P1和P2之間共享一個公用訊號量S,並且賦其初值為0,將signal(S)操作放在語句S1後,而把S2語句前面插入Wait(S)操作。 虛擬碼:
在程序P1中: S1; signal(S);
在程序P2中:wait(S);s2;
三. 司機售票員問題
1. 問題描述
公共汽車上。司機和售票員的活動分別如下: 司機的活動:啟動車輛,正常行車,到站停車. 售票員的活動: 關車門,售票,開車門。
2. 思路
分析可知。售票員和司機的4個活動存在前後關係。即關車門–>啟動汽車。到站停車–>開車門。所以用兩個訊號量s1,s2分別來控制兩組前後關係.
3. 使用記錄型訊號量解決問題
虛擬碼
semaphore s1 = 1,s2 = 1;
//司機
void driver(){
signal(s1)
啟動車輛
正常行駛
到站停車
wait(s2)
}
void conductor(){
關車門
wait(s1)
售票
signal(s2)
開門
}
四. 生產者消費者(快取繫結問題)問題
1. 問題描述
有兩個程序:一組生產者程序和一組消費者程序共享一個初始為空、固定大小為n的快取(緩衝區)。生產者的工作是製造一段資料,只有緩衝區沒滿時,生產者才能把訊息放入到緩衝區,否則必須等待,如此反覆; 同時,只有緩衝區不空時,消費者才能從中取出訊息,一次消費一段資料(即將其從快取中移出),否則必須等待。由於緩衝區是臨界資源,它只允許一個生產者放入訊息,或者一個消費者從中取出訊息。
2. 思路
問題的核心是:
- 要保證不讓生產者在快取還是滿的時候仍然要向內寫資料;
- 不讓消費者試圖從空的快取中取出資料。
生產者和消費者對緩衝區互斥訪問是互斥關係,同時生產者和消費者又是一個相互協作的關係,只有生產者生產之後,消費者才能消費,他們也是同步關係。
3. 使用訊號量解決問題
說明:
假定在生產者和消費者之間的公用快取池具有n個緩衝區,這時候可利用互斥訊號量mutex來實現諸程序對快取池的互斥利用;利用訊號量的empty和full分別表示快取池中空快取區和滿快取區的數量。
虛擬碼描述:
int in=0,out=0;
item buffer[n];
semaphore mutex =1,empty = n,full = 0;
//生產者
void producer(){
do{
producer an item nextp;
···
wait(empty); //詢問是否有空快取區
wait(mutex); //互斥訪問
buffer[in] = nextp;
in = (in+1)%n;
signal(mutex);//解除互斥訪問
signal(full); //釋放一個滿快取區,滿快取+1
}while(TRUE)
}
//消費者
void consumer(){
do{
wait(full);
wait(mutex);
nextc = buffer[out];
out = (out+1) %n;
signal(mutex);
signal(empty);
}while(TRUE)
}
當然也可以使用AND訊號量解決問題:即用Swait(empty,mutex)來代替wait(empty);wait(mutex); 用Ssignal(mutex,full)來代替signal(mutex)和signal(full);
五. 哲學家進餐問題
1. 問題描述
有五個哲學家公用一張圓桌,分別坐在周圍的五個椅子上,在圓桌上有五個碗和五隻筷子,他們的生活方式就是交替的進行思考和進餐。平時,一個哲學家進行思考,飢餓是便試圖取其左右最靠近他的筷子,只有在他同時拿到兩隻筷子時才能進餐。進餐畢,放下筷子繼續思考
2. 思路
分析可知,筷子是臨界資源,在一段時間內只允許一位哲學家使用,為了實現對筷子的互斥使用,為了實現對筷子的互斥使用,可以用一個訊號量表示一隻筷子,由這五個訊號量構成訊號量陣列。
3. 使用記錄型訊號量解決問題
虛擬碼
semaphore chopstick[5] = {1,1,1,1,1};
//第i位哲學家的活動可描述為:
do{
wait(chopstick[i]);
wait(chopstick[(i+1)%5]);
···
//eat
···
signal(chopstick[i]);
signal(chopstick[(i+1)%5];
···
//think
···
}while(TRUE)
4.分析
上面的解法雖然保證了相鄰的哲學家不會同時進餐,但可能會導致死鎖問題,比如說,當5個哲學家同時飢餓而去拿左邊的筷子是,就會發生死鎖。
避免死鎖的方法:
- 至多隻允許有4位哲學家同時去拿左邊的筷子,最終能保證至少有一位哲學家能夠進餐。
- 僅當哲學家左右兩隻筷子同時可用時,才允許他拿起筷子進餐。、
- 規定奇數號哲學家先拿他左邊的筷子,然後再去拿他右邊的筷子,而偶數號哲學家則相反
六. 讀者寫者問題
1. 問題描述
一個數據檔案可以被多個檔案共享,我們把只要求讀檔案的程序稱為 “Reader程序”,其他程序則稱為“Writer程序”。允許多個程序讀一個檔案,但不允許一個Writer程序和其他Reader程序或者Writer程序同時訪問共享。
2. 思路
由題意可知,讀者之間不互斥,寫者之間互斥,寫者和讀者之間互斥。所以為寫者之間,寫者和讀者之間設定互斥訊號量wmutex。在設定一個整形變數readcount表示讀程序的程序數目。當readcount=0時,才允許寫者執行·。對於讀者而言readcount是一個可被多個讀者訪問的臨界資源。因此,也應該為它設定一個互斥訊號量rmutex。
3. 使用記錄型訊號量解決問題
虛擬碼
semaphore rmutex = 1,wmutex = 1;
int readcount = 0;
void reader(){
do{
wait(rmutex);
if(readcount==0) wait(wmutex);
readcount++;
signal(rmutex);
···
perform read operation;
···
wait(rmutex);
if(readcount==0) signal(wmutex);
readcount--;
signal(rmutex);
}while(TRUE);
}
void writer(){
do{
wait(wmutex);
perfrom write operation;
signal(wmutex);
}while(TRUE);
}