作業系統——程序同步互斥經典題目
前言
這裡是作業系統課程中老師佈置的作業,主要是關於程序同步互斥的考研真題。
題目
題目一
有4個程序P1、P2、P3、P4。要求P1必須在P2、P3開始前完成,P2、P3必須在P4開始前完成,且P2和P3 不能併發執行。試寫出這4個程序的同步互斥演算法。
解答:
-
題目分析:
-
同步關係有:
- P1->P2
- P1->P3
- P2,P3 -> P4
-
互斥關係有:
- P2 - P3
可以根據題目畫出下面的前驅圖
-
-
解答
因此,我們設定四個訊號量:S1、S2、S3。其中具體含義如下
S1:初始為0,P1是否完成,同時充當P2和P3的互斥訊號量
S2:初始為0,P2是否完成
S3:初始為0,P3是否完成
Semaphore S1 = 0; Semaphore S2 = 0; Semaphore S3 = 0; P1(){ while(1){ P1操作; V(S1); } } P2(){ while(1){ P(S1); P2操作; V(S1); V(S2); } } P3{ while(1){ P(S1); P3操作; V(S1); V(S3); } } P4{ while(1){ P(S2); P(S3); P4操作; V(S2); V(S3); } }
-
擴充套件:假如P2和P3可以並行,應該如何寫?
略
題目二
假設有一個路口,通行交通規則如下:只要沒有機動車在通行,路口行人就可以透過,只有沒有行人在透過路口且沒有其他機動車在透過路口時該機動車才能透過。請用P、V操作描述行人和機動車透過路口的同步互斥過程。
-
首先對題目進行分析:
這個題目類似於讀者問題,行人可以存在多個,但機動車彼此之間只能存在一個。
而且行人與機動車是互斥的。
-
行人透過 與 機動車透過 屬於 互斥關係
-
機動車透過 與 (其他)機動車透過也屬於互斥關係
-
無同步關係
行人之間是沒有互斥關係,可以存在多個行人,所以不能單純使用PV操作單個互斥向量管理行人程序
-
-
解答:
int count = 0; // 記錄行人數量 Semaphore mutex = 1; // 保護count更新 cross = 1; //機動車、行人的互斥 P_car(){ while(1){ P(cross); 機動車透過路口(); V(cross); } } P_passerby(){ while(1){ P(mutex); if(count==0){ //如果路口上沒有行人,代表可能有機動車,需要進行檢測 P(cross); } count++; V(mutex); 行人透過路口(); P(mutex); count--; if(count == 0) { V(cross); } V(mutex); } }
題目三
系統中有多個生產者程序和消費者程序,共享用一個可以存1000個產品的緩衝區(初始為空)。
- 當緩衝區未滿時,生產者程序可以放入1件其生產的產品,否則等待;
- 當緩衝區不空時,消費者程序可以取走1件產品,否則等待。
要求1個消費者程序從緩衝區連續取出10件產品後,其他消費者程序才可以取產品,請用訊號量P,V(或wait()、signal())操作實現程序間的互斥和同步,要求寫出完整的過程;並指出所用訊號量的含義和初值。
-
分析:
- 有多個生產者程序,互相併不互斥,但是其檢查緩衝區剩餘容量時需要互斥管理
- 消費者程序必須要取出10件產品,才能釋放資源,因此互相互斥
- 生產者程序和消費者程序之間對緩衝區保持互斥,防止併發導致資訊過期
-
解答:
Semaphore mutex = 1; //保護緩衝區訪問 Semaphore full = 0; //表示緩衝區中的產品數量 Semaphore empty = 1000; //緩衝區空餘位置 Semaphore consum = 1; //消費者執行緒之間不取出10個之前保持互斥 producer(){ while(1){ 生產一個產品(); P(mutex); P(empty); 將生產的產品存入緩衝區(); V(full); V(mutex); } } consumer(){ while(1){ P(consum); for(int i=0;i<10;i++){ //取出10個產品 P(mutex); P(full); 取出產品(); V(empty); V(mutex); } V(consum); 使用產品(); } }
題目四
一組相互合作的程序P1、P2、P3、P4、P5、P6,其執行過程須滿足如圖所示的同步關係,請使用訊號量機制對該組程序進行同步。
Semaphore S1 = 0;
Semaphore S2 = 0;
Semaphore S3 = 0;
Semaphore S4 = 0;
Semaphore S5 = 0;
P1(){
while(1){
P1操作();
V(S1);
V(S2);
}
}
P2(){
while(1){
P(S1);
P2操作();
V(S3);
}
}
P3(){
while(1){
P(S2);
P3操作;
V(S4);
}
}
P4(){
while(1){
P(S3);
P4操作();
V(S5);
}
}
P5(){
while(1){
P(S4);
P(S5);
P5操作();
V(S6);
}
}
P6(){
while(1){
P(S6);
P6操作();
}
}
題目五
兩個程序 P1、P2 併發執行,並用訊號量 M1,M2 分別實現對兩個互斥共享的資源R1 和 R2 的互斥訪問。這兩個程序以什麼次序執行會導致死鎖?在不影響程式功能的情況下,請修改演算法以防止死鎖,同時儘可能保持較高的資源利用率。
var M1, M2: semaphore = 1, 1;
begin
parbegin
P1
begin:
wait(M1);
access R1;
wait(M2);
access R1 and R2;
signal(M1);
signal(M2);
end
P2
begin:
wait(M2);
access R2;
wait(M1);
access R1;
signal(M1);
signal(M2);
end
parend
解答:
導致死鎖的順序: P1獲取到了R1資源,但還沒有得到M2訊號量的時候,P2使用了M2訊號量。這時P1想獲取M2,但M2被P2持有;P2想獲取M1,但M1被P1持有。兩者都不釋放自己的資源,但又持有著對面想要的資源。
var M1, M2: semaphore = 1, 1;
begin
parbegin
P1
begin:
wait(M1);
access R1;
wait(M2);
access R1 and R2;
signal(M1);
signal(M2);
end
P2
begin:
wait(M1);
access R1;
wait(M2);
access R2;
signal(M1);
signal(M2);
end
parend
題目六
有 n(n≤3)位哲學家圍坐在一張圓桌邊,每位哲學家交替地就餐和思考。在圓桌中心有 m(m≥1)個碗,每兩位哲學家之間有 1 根筷子。每位哲學家必須取到一個碗和兩側的筷子之後,才能就餐,進餐完畢,將碗和筷子放回原位,繼續思考。為使盡可能多的哲學家同時就餐,且防止出現死鎖現象,請使用訊號量的 P、V(或 wait()、signal())操作描述上述過程中的互斥與同步,並說明所用訊號量及初值的含義。
semaphore bow = m; // 碗的訊號量
semaphore chopstick[n] = 1; // 筷子的訊號量
void philosopher(int i) {
while (true) {
think();
P(bow); // 嘗試獲取一個碗
if(i%2 == 0){
P(chopstick[(i+1)%n]);//偶數哲學家先右後左
P(chopstick[i]);
}else{
P(chopstick[i]);//奇數哲學家先左後右
P(chopstick[(i+1)%n]);
}
eat();
V(bow);
V(chopstick[i]);
V(chopstick[(i+1)%n]);
}
}
題目七
現有 5 個操作 A、B、C、D 和 E。操作 C 必須在 A 和 B 完成後執行,操作 E 必須在C 和 D 完成後執行,請使用訊號量的 P、V(或 wait()、signal())操作描述上述操作之間的同步關係,並說明所用訊號量及其初值。
semaphore A = 0; // 表示 A 是否已完成
semaphore B = 0; // 表示 A 是否已完成
semaphore C = 0; // 表示 A 是否已完成
semaphore D = 0; // 表示 A 是否已完成
void operationA() {
while(1){
A操作();
V(A); // 操作 A 完成
}
}
void operationB() {
while(1){
B操作();
V(B); // 操作 B 完成
}
}
void operationC() {
while(1){
P(A); // 等待 A 和 B 完成
P(B);
C操作();
V(C); // 操作 C 完成
}
}
void operationD() {
while(1){
D操作();
V(D); // 操作 D 完成
}
}
void operationE() {
while(1){
P(C);
P(D);
E操作();
V(E);
}
}
題目八
某展覽館舉行現代畫展,展覽館內可以同時接納 2000 人參觀,參觀者分為學生票和普通票,要求
(1)0≤普通票-學生票≤400;
(2)展覽館出入口每次只有 1 人進或出。
請用P、V(或 wait()、signal())操作描述持普通票和持學生票者進、出展覽館的同步互斥過程。
分析:
①不等式:展館內普通票比學生票多,且最多多400
②若展館內無學生票,普通票最多400,設初值為400;若無普通票,則也必然沒有學生票,設學生票初值0。
③展館內每多一張普通票,學生票就多一個入館機會,普通票每出館一人,學生票就少一個入館機會。
semaphore Pticket = 400;//可使用普通票資源
semaphore Sticket = 0; //可使用學生票資源
semaphore empty = 2000; //可入館人數
semaphore mutex = 1;//出入口
Ordinary()//普通票
{
while(1)
{
p(Pticket); //獲取普通票入館資源
p(mutex); //進出互斥
p(empty);
入館;
v(mutex);
v(Sticket);//學生票多了一次入館機會
參觀展館;
p(Sticket); //獲取學術票入館資源
p(mutex); //進出互斥
出館;
v(mutex);
v(Pticket);
v(empty);
}
}
Student()
{
while(1)
{
p(Sticket);
p(empty);
p(mutex);
進館;
v(mutex);
v(Pticket);
參觀展館:
p(Pticket);
p(mutex);
出館;
v(mutex);
v(Sticket);
v(empty);
}
}
題目九
有 3 個程序 P1、P2和 P3協作解決檔案列印問題。
P1將檔案記錄從磁碟讀入記憶體的緩衝區 1,每執行一次讀一個記錄;
P2將緩衝區 1 中的內容複製到緩衝區 2 中,每執行一次複製一個記錄;
P3將緩衝區 2 中的內容列印出來,每執行一次列印一個記錄。
緩衝區的大小與記錄大小一樣。請用訊號量來保證檔案的正確列印。
semaphore mutex1 = 1; //對於緩衝區1的訪問互斥
semaphore full1 = 0; //緩衝區1的內容
semaphore mutex2 = 1; //對於緩衝區2的訪問互斥
semaphore full2 = 0; //緩衝區2的內容
void P1(){
while(true){
P(mutex1);
從檔案記錄讀入緩衝區1();
V(full1);
V(mutex1);
}
}
void P2(){
while(true){
P(full1)
P(mutex1);
讀取緩衝區1();
V(mutex1);
P(mutex2);
寫入緩衝區2();
V(full2);
V(mutex2);
}
}
void P3(){
while(true){
P(full2);
P(mutex1);
讀取緩衝區2();
V(mutex1);
列印();
}
}
題目十
桌上有個能盛得下 1 個水果的空盤子。爸爸不停地向盤中放蘋果,媽媽不停地向盤中放桔子,兒子不停地從盤中取出桔子享用,女兒不停地從盤中取出蘋果享用。試用訊號量實現媽媽、爸爸、兒子和女兒迴圈程序之間的同步。
semaphore plate = 1; //對盤子是否為空
semaphore orange = 0; //橘子資源
semaphore apple = 0; //蘋果資源
void dad(){
while(1){
P(plate);
放入蘋果();
V(apple);
}
}
void mom(){
while(1){
P(plate);
放入橘子();
V(orange);
}
}
void son(){
while(1){
P(orange);
V(plate);
享用();
}
}
void daughter(){
while(1){
P(apple);
V(plate);
享用();
}
}
題目十一
試用記錄型訊號量寫出一個不會死鎖的哲學家進餐問題的演算法。
程式碼與題目六一樣。
semaphore bow = m; // 碗的訊號量
semaphore chopstick[n] = 1; // 筷子的訊號量
void philosopher(int i) {
while (true) {
think();
P(bow); // 嘗試獲取一個碗
if(i%2 == 0){
P(chopstick[(i+1)%n]);//偶數哲學家先右後左
P(chopstick[i]);
}else{
P(chopstick[i]);//奇數哲學家先左後右
P(chopstick[(i+1)%n]);
}
eat();
V(bow);
V(chopstick[i]);
V(chopstick[(i+1)%n]);
}
}
題目十二
設公共汽車上,司機和售票員的活動分別是:司機活動:啟動車輛、正常行車、到站停車;售票員活動:關車門、售票、開車門。
要求:當發車時間到,售票員關好車門後,司機才能啟動車輛,售票員開始售票;當到站後,司機停車後,售票員才能開啟車門,乘客下車,站牌乘客上車。
在汽車不斷地到站、停車、行駛過程中,這兩個活動有什麼同步關係?用訊號量和 P、V 操作實現他們的同步。
分析:
正常行車、到站停車->售票員開啟車門->乘客下車,站牌乘客上車->售票員關好車門->啟動車輛->回到起始點
semaphore door = 0;
semaphore stop = 0;
void conductor()
{
while(true)
{
//乘客上下車
//關門
V(door);//售票員給司機關門的訊號
//此階段為售票時間
P(stop);//等待停車訊號,一旦停車,則開門
//開門
}
}
void driver()
{
while(true)
{
P(door);//司機等待關門訊號,一旦獲取訊號,則啟動車輛
//此階段為正常行車時間
V(stop);//司機給售票員停車的訊號
}
}
題目十三
在一個只允許單向行駛的十字路口,分別有若干由東向西,由南向北的車輛在等待透過十字路口。為了安全,每次只允許一輛車透過(東→西或南→北)。當有車輛透過時其它車輛等待,當無車輛在路口行駛時則允許一輛車(東→西或南→北)進入。請用 P、V 操作實現能保證安全行駛的自動管理系統。
semaphore mutex = 1;
void car(){ //兩種方向的車都一樣
while(1){
P(mutex);
透過();
}
}
題目十四
設有一個具有 N 個資訊元素的環形緩衝區,A 程序順序地把資訊寫入緩衝區,B 程序依次地從緩衝區中讀出資訊。回答下列問題:(1)敘述 A、B 兩個程序的相互制約關係。(2)用 P、V 操作表示 A、B 程序的同步演算法。
分析:
A在寫入資訊時,B不能讀資訊;相反,B在讀資訊時,A不能寫入資訊。
當緩衝區滿時,A不能寫入資訊。
當緩衝區為空時,B不能讀出資訊。
semaphore empty = N; //緩衝區剩餘空間
semaphore full = 0; //緩衝區的資訊數量
semaphore mutex = 1; //緩衝區的互斥訪問
void A(){
while(true){
P(empty);
P(mutex);
寫入資訊();
V(mutex);
V(full);
}
}
void B(){
while(true){
P(full);
P(mutex);
讀取資訊();
V(mutex);
V(empty);
}
}
題目十五
某博物館最多可容納 500 人同時參觀,有一個出入口,該出入口一次僅允許一個人透過。
參觀者的活動描述如下:
Cobegin
參觀者程序 i:
{
…
進門;
…
參觀;
…
出門;
…
}
Coend
請新增必要的訊號量和 P、V(或 wait()、signal( ))操作,以實現上述操作過程中的互斥與同步。要求寫出完整的過程,說明訊號量含義並賦初值。
Cobegin
semaphore empty = 500; //博物館空位數
semaphore mutex = 0; //一次只能一個人進出
參觀者程序 i:
{
P(empty); //當博物館還有空位的時才能進入博物館
P(mutex);
進門; //當博物館還有空位的時才能進入博物館
V(mutex);
參觀;
P(mutex);
出門; //當博物館還有空位的時才能進入博物館
V(mutex);
V(empty); //出門後釋放一個空位
}
Coend