作業系統(2.3程序同步)本章最後結合記錄型訊號量的使用方法和例題進行了詳細講解。
最近在準備推免的面試把王道的程序這一章拿出來做了一下,收穫挺多的,寫個文章總結下
2.3程序同步
訪問臨界資源過程
do{
entry section;//進入區 設定訪問臨界區標誌
critical section;//臨界區 訪問臨界資源
exit section;//退出區 將訪問臨界區標誌清除
remainder section;//剩餘區
}while(true)
軟體實現臨界區互斥
1.單標誌法
單標誌法主要問題是對臨界區資源的訪問各個程序需要交替進行,如果一個程序完成後,及時臨界區可以訪問,另一個程序也無法進入。這就違背了“空閒讓進”原則
P0程序: while(turn!=0); critical section; turn=1; remainder section; p1程序: while(turn!=1); critical section; turn =0; remainder section;
2.雙標誌法先檢查
先判斷對方是否在訪問臨界區,然後再決定自己是否進入,它都問題是如果按照1 2 3 4的順序執行的話,雙法都進入臨界區,這樣就違背了忙則等待的原則
pi程序
while(flag[j));//1
flag[i]=TRUE;//3
critical section;
flag[i]=FALSE;
remainder section;
pj程序
while(flag[i]);//2
flag[j]=TRUE;//4
critical section;
flag[j]=FALSE;
remainder section;
3.雙標誌後檢查法
先將臨界區設定為自己的,再判斷對方是否訪問,這個方法的問題是如果按照1 2 3 4 執行的話,3,4 都會永遠處於真,這樣會導致飢餓問題
pi程序
flag[i]=TRUE;//1
while(flag[j]);//3
critical section;
flag[i]=FALSE;
remainder section;
pj程序
flag[j]=TRUE;//2
while(flag[i]);//4
critical section;
flag[j]=FALSE:
remainder section;
4.Peterson‘s Algorithm
該演算法的核心是通過增加turn讓後執行的turn賦值語句,決定誰先進入臨界區
pi程序 flag[i]=TRUE;//1 turn=j; while(flag[j]&&turn==j);//3 critical section; flag[i]=FALSE; remainder section; pj程序 flag[j]=TRUE;//2 turn=i; while(flag[i]&&turn==i);//4 critical section; flag[j]=FALSE: remainder section;
硬體實現方法
1.中斷遮蔽方法
因為cpu只有在發生中斷的時候才能進行程序切換,因此關中斷就可以保證臨界區資源順利訪問
2.硬體指令
TestAndSet指令
Swap 指令
優點:適用於任意數目的程序,不管是單處理機還是多處理機:簡單,容易驗證其正確性,可以支援程序內有多個臨界區,只需為沒給臨界區設立一個訊號量即可。
缺點:程序等待進入臨界區時需要消耗處理機時間,不能實現讓權等待,從等待進入程序中隨機選擇一個進入臨界區,有的程序可能一直選不上,從而導致”飢餓“現象。
訊號量
訊號量只能被兩個標準的原語wait(S)和signal(S)來訪問,也可被記錯P/V
1.整型訊號量
一個用於表示資源數目的整型量S,wait和signal操作可描述為下方,問題是wait中如果S<=0,就會不斷嘗試,因此該機制並未遵循“讓權等待”原則
wait(S){
while(S<=0);
S=S-1;
}
signal(S){
S=S+1;
}
2.記錄型訊號量
記錄型訊號量通過一個記錄程序的佇列來避免重複嘗試。
記錄型訊號量資料結構
typedef struct{
int value;
struct process *L;
}semaphore;
void wait(semaphore S){
S.value--;
if(S.value<0){
block(S.L);
}
}
void signal(semaphore S)
S.value++;
if(S.value<=0){
wakeup(S.L);
}
}
重要概念
1.臨界資源:一次僅允許一個程序使用的資源。
2.臨界區:程序中用於訪問共享資源的程式碼。
3.臨界資源是互斥共享資源,非共享資料不屬於臨界資源。
4.可重入程式碼(Reentry code)也叫純程式碼(Pure code)是一種允許多個程序同時訪問的程式碼。為了使各程序所執行的程式碼完全相同,故不允許任何程序對其進行修改
5.公用佇列可供多個程序使用,但一次只可有一個程序使用。
6.P,V 操作是一種低階的程序通訊原語,它是不能被中斷的,也不是系統呼叫的。
7.執行P操作可能導致程序阻塞,但是程序在執行這個操作時還是執行狀態。
8.用P,V操作實現程序同步,訊號量的初值由使用者確定。若期望的訊息尚未產生,則置初值為 0,若期望的訊息已經存在,則置為條件中的數值,比如讀者寫者問題,empty因為已經存在置為n,full開始時不存在就置為0。
9.管程不僅能實現程序間的互斥,而且能夠實現程序間的同步。
10.原語是指完成某種功能且不被分割不被中斷執行的操作序列,通常可由硬體來實現完成不被分割執行特性的功能。在單處理機時還可以通過軟體關閉中斷的方式。
問題
1.訊號量的初始值問題?
訊號量有兩種資源訊號量和互斥訊號量,同步訊號量初始值為1,小於0時,其絕對值為在等待進入該臨界區的程序數。資源訊號量初始值為該臨界資源的數目,小於0時,其絕對值為在等待該臨界資源的程序數,大於0時,其絕對值為該臨界資源的剩餘數。
2. 管程wait/signal與PV操作
訊號量的P操作可能阻塞,也可能不阻塞;而管程的wait操作一定會阻塞。訊號量的V操作如果喚醒了其他執行緒,當前執行緒與被喚醒執行緒併發執行;對於管程的signal操作,要麼當前執行緒繼續執行,要麼被喚醒執行緒繼續執行,二者不能併發。 同時訊號量的V操作一定會改變訊號量S的值(S=S+1)。管程中的signal操作是針對某個變數的,如果不存在因該條件而阻塞的程序,signal不會產生任何影響。
訊號量的利用
1.訊號量的同步
要求P1執行X語句後才可執行P2 Y語句,通過在將S=0,和在Y語句上方放置P操作,來限制P2執行,通過在P1 X語句後放置V操作來使P2可以繼續執行,見程式碼。
semaphore S=0;
P1(){
x;
V(S);
}
P2(){
P(S);
y;
}
2.訊號量的互斥
通過將S=1和在對臨界區資源上方放置P操作來實現,因為一旦有個程序實現了P操作別的程序就P操作就會組織他們進入臨界區只能等第一個程序的V操作
semaphore S=1;
P1(){
P(S);
臨界區;
V(S);
}
P2(){
P(S);
臨界區;
V(S);
}
3 訊號量的前驅圖(同步的加強版)
只需根據每個節點的出度入度即可得知P/V操作的數量,同時每條邊都是一個同步關係需要設定一個訊號量,出度是V操作,入度是P操作,參考案例
S1->S2 a1
S1->S3 a2
S2->S4 b1
S2->S5 b2
S3->S6 c
S4->S6 d
S5->S6 e
semaphore a1=a2=b1=b2=c=d=e=0;
S1(){
...;
V(a1);V(a2);
}
S2(){
P(a1);
...;
V(b1);V(b2);
}
S3(){
P(a2);
...;
V(c);
}
S4(){
P(b1);
...;
V(d);
}
S5(){
P(b2);
...;
V(e);
}
S6(){
P(c);
P(d);
P(e);
...;
}
學習了訊號量的利用讓我們將他們融匯貫通吧
經典同步問題
1.生成者消費者問題
問題描述
一組生產者程序和一個消費者程序共享一個初始為空,大小為n的緩衝區,只有緩衝區沒滿時,生產者才能把訊息放入到緩衝區,否則必須等待;只有緩衝區不空時,消費者才能從中取出訊息,否則必須等待。由於緩衝區是臨界資源,它只允許一個生產者放入訊息,或者一個消費者從中取出訊息。
關係分析:生產者於消費者因為有先後順序,所以是同步關係,因為對緩衝區的爭奪所以也是互斥關係。
思路:設定一個mutex=1來實現對緩衝區的互斥反問,要知道緩衝區對於生產者的資源是空多少,他的初始值是empty=n,緩衝區對消費者的資源是滿多少,顧設定full=0;
semaphore mutex=1;
semaphore empty=n;
semaphore full=0;
producer(){
while(True){
P(empty)
P(mutex)
put();
V(mutex)
V(full);
}
}
consumer(){
while(True){
P(full)
P(mutex)
put();
V(mutex)
V(empty);
}
}
註解:有的人可能會問為什麼mutex要在full或者empty的裡面,可以這麼理解,他們各自都必須先確定自己可以對緩衝區操作才能佔用緩衝區,否則會出現死鎖,比如一開始對消費者如果mutex先鎖定緩衝區,然後p(full)發現緩衝區為空被阻塞,但因為他對緩衝區的鎖定使得生成者也沒有機會進入,導致死鎖。
生產者-消費者變種題1
問題描述:桌子上有一隻盤子,每次只能向其中放入一個水果,爸爸專門向盤子中放入蘋果,媽媽專門向盤子中放入橘子,兒子專等吃盤子中的橘子,女兒專等吃盤子中的蘋果。只有盤子為空時,爸爸或媽媽就可向盤子中放一個蘋果,僅當盤子中的有自己需要的水果時,兒子或女兒可以從盤子中取出。
關係分析:爸爸媽媽之間對盤子是互斥的,爸爸女兒和媽媽兒子是同步的
思路分析:設定mutex=1互斥訪問盤子,設定emptyA=1,fullA=0,同步爸爸女兒,設定emptyB=1,fullB=0,同步媽媽兒子。
semaphore mutex=1;
semaphore emptyA=1,fullA=0;
semaphore emptyB=1,fullB=0;
dad(){
while(True){
P(emptyA)
P(mutex)
putApple();
V(fullA);
}
}
Mom(){
while(True){
P(emptyB);
P(mutex);
putJuice();
V(fullB);
}
}
daughter(){
while(True){
P(fullA);
getApple();
V(mutex);
V(emptyA);
}
}
son(){
while(True){
P(fullB);
getJuice();
V(mutex);
V(emptyB);
}
}
上方為標準解法但是仔細想想是可以簡化的,因為mutex等價於empty,因為如果獲得盤子權力,那必是兒子,女兒已經拿掉了,通過V操作喚醒的,那盤子必定是空,可以放入無需檢查,因此去掉兩個empty仍然正確。
semaphore mutex=1;
semaphore emptyA=1,fullA=0;
semaphore emptyB=1,fullB=0;
dad(){
while(True){
P(mutex)
putApple();
V(fullA);
}
}
Mom(){
while(True){
P(mutex);
putJuice();
V(fullB);
}
}
daughter(){
while(True){
P(fullA);
getApple();
V(mutex);
}
}
son(){
while(True){
P(fullB);
getJuice();
V(mutex);
}
}
生產者-消費者變種問題2
(2009年計算機聯考真題)三個程序P1,P2,P3互斥使用一個包含N個單元的緩衝區,P1每次用produce()生成一個正整數並用put()送入緩衝區某一空單元中;P2每次用getodd()從該緩衝區中取出一個奇數並用countodd()統計奇數個數;P3每次用geteven()從該緩衝區中取出一個偶數並使用counteven()統計偶數個數,請使用訊號量機制實現三個程序的同步與互斥活動,並說明所定義的訊號量的含義。
關係分析:P1與P2/P3是同步,他們之間因為對緩衝區的爭奪是互斥
思路分析:設定mutex=1實現對緩衝區的互斥訪問,設定empty=n和full=0來實現同步。
semaphore mutex=1;
semaphore empty=n;
semaphore pA=0;
semaphore pB=0;
P1(){
while(True){
P(empty);
x=produce();
P(mutex);
put();
V(mutex);
if(x%2==0){
V(pB)
}else{
V(pA);
}
}
}
P2(){
while(True){
P(pA);
P(mutex);
getodd();
V(mutex);
V(empty)
countodd();
}
}
P3(){
while(True){
P(pB);
P(mutex);
geteven();
V(mutex);
V(empty);
counteven();
}
}
生產者-消費者變種問題3
在一個倉庫中可以存放A和B兩種產品,要求:
(1)每次只能存入一種產品。
(2)A產品數量-B產品數量<M
(3) B產品數量-A產品數量<N
其中M,N是正整數,試用P操作,V操作描述產品A與產品B的入庫過程。
思路分析:A最多能生產的數量肯定是M-1,B最多能生產的數量最多肯定是N-1,同時A生成
semaphore AB=M-1;
semaphore BA=N-1;
semaphore mutex=1;
P1(){
while(True){
P(AB)
P(mutex)
A();
V(mutex)
V(BA);
}
}
P2(){
while(True){
P(BA)
P(mutex)
B();
V(mutex)
V(AB);
}
}
生產者-消費者變種問題4
某個工廠有兩個生產車間和一個裝配車間,兩個生產車間分別生產A,B兩種零件,裝配車間的任務是把A,B兩種零件組裝成產品。兩個生產車間每生產一個零件後都要分別把它們送到裝配車間F1,F2上,F1存放零件A,F2存放零件B,F1和F2的容量均可以存放10個零件。裝配工人每次從貨架上取出一個零件A和一個零件B後組裝成產品。請用P,V操作進行正確管理
關係分析:裝配車間為消費者,生產A,B兩種零件的是車間是生產者。
思路分析:這道題唯一不同就是多了一個組裝,騎士只要把獲取兩種產品的過程寫在一起就是組裝了,mutexA=1,mutexB=1,對車間的互斥訪問,emptyA=10,emptyB=10,fullA=0,fullB=0。
semaphore emptyA=10,emptyB=10;
semaphore fullA=0,fullB=0;
semaphore mutexA=1,mutexB=1;
PA(){
while(True){
P(emptyA);
P(mutexA);
putA();
V(mutexA);
V(fullA);
}
}
PB(){
while(True){
P(emptyB);
P(mutexB);
putB();
V(mutexB);
V(fullB);
}
}
PC(){
while(True){
P(fullA);
P(mutexA);
getA();
V(mutexA);
V(emptyA);
P(fullB);
P(mutexB);
getB();
V(mutexB);
V(emptyB);
//組裝
}
}
生產者消費者變種問題5
某寺廟,有小和尚,老和尚若干,有一水缸,由小和尚提入水缸供老和尚飲用,水缸可容納10桶水,水取自同一井中,水井徑窄,每次只能容納一桶取水,水桶總數為3個,每次入缸取水僅為1桶水,且不可同時進行。試給出有關從缸取水,入水的演算法描述。
關係分析:小和尚和老和尚之間喝水為同步,同時對水缸為互斥,對桶進行爭奪,小和尚之間對井的爭奪也是互斥
思路分析:井互斥mutexJ=1;缸互斥mutexG=1,桶資源mutex=3;full=0,empty=10;
semaphore mutexJ=1,mutexG=1,Tong=3,full=0,empty=10;
Psmall(){
while(True){
P(empty);
P(Tong)
P(mutexJ);
//取水
P(mutexJ);
P(mutexG);
put();
V(mutexG);
V(full);
V(Tong);
}
}
Podd(){
while(True){
P(full);
P(Tong);
P(mutexG);
get();
V(mutexG);
V(empty);
//喝水
V(Tong);
}
}
生產者消費者變種問題6
設P,Q,R共享一個緩衝區(該緩衝區大小為1),P,Q構成一對生產者-消費者,R既為生產者又為消費者,使用P,V操作實現其同步。
思路分析:R既為消費者又為生產者,則必須在執行前判斷狀態,若empty=1,則執行生產者功能,若full==1,執行消費者功能
semaphore full=0;
semaphore empty=1;
semaphore mutex=1;
Procedure P
{
while(TRUE){
P(empty);
P(mutex);
put();
V(mutex);
V(full);
}
}
Procedure Q
{
while(TRUE){
P(full);
P(mutex);
get();
V(mutex);
V(empty);
}
}
Procedure R
{
while(TRUE){
if(empty==1){
P(empty);
P(mutex);
put();
V(mutex);
V(full);
}
if(full==1){
P(full);
P(mutex);
get();
V(mutex);
V(empty);
}
}
生產者消費者變種問題7
設自行車生產線上有一隻箱子,其中有N個位置(N>=3),若每個位置可存放一個車架或一個車輪,又設有三個工人,其活動分別為:
工人1活動:
do{
加工一個車架;
車架放入箱中;
}while(1)
工人2活動:
do{
加工一個車輪;
車架放入箱中;
}while(1)
工人3活動:
do{
箱中取出一個車架;
箱中取二個車輪;
組裝為一臺車
}while(1)
試分別用訊號量宇PV操作實現三個工人的合作,要求解中不含死鎖
思路分析:本體需要知道一個限制雖然成產線有N個位置但是車輪的最大數目卻是N-1,車架是N-2,否則會出現死鎖。同時需要設定一個mutex=1來互斥訪問緩衝區,設定
semaphore mutex=1;
semaphore empty1=N-2;
semaphore full1=0;
semaphore empty2=N-1;
semaphore full2=0;
semaphore empty=N;
do{
P(empty1);
P(empty);
加工一個車架;
P(mutex);
車架放入箱中;
V(mutex);
V(full1);
}while(1)
工人2活動:
do{
P(empty2);
P(empty);
加工一個車輪;
P(mutex);
車輪放入箱中;
V(mutex);
V(full2);
}while(1)
工人3活動:
do{
P(full1);
P(mutex);
箱中取出一個車架;
V(mutex);
V(empty);
V(empty1);
P(full2);
P(full2);
P(mutex);
箱中取一個車輪;
V(mutex);
V(empty);
V(empty);
V(empty2);
V(empty2);
組裝為一臺車
}while(1)
註釋:本體沒有強制要求去拿互斥,可以去掉mutex
生產者消費者變種問題8
(2015年計算機聯考題)有A,B兩個人通過信箱進行辯論,每個人都從自己的信箱中國呢取得對方的問題。將答案和向對方提出的新問題組成一個郵件放入對方的郵箱中。假設A的信箱最多放M個郵件,B的信箱最多放N個郵件,初始時A的信箱中有x個郵箱(0<x<M),B的信箱中有y個(0<y<N),辯論者每次取出一個郵件,郵件數減1,A和B兩個人的操作過程描述如下
CoBegin
A{
while(TRUE){
從A的信箱中取出一個郵件;
回答問題並提出一個新問題;
將新郵件放入B的信箱;
}
}
B{
while(TRUE){
從B的信箱中取出一個郵件;
回答問題並提出一個新問題;
將新郵件放入A的信箱;
}
}
當信箱不為空時,辯論者才能從信箱中取郵件,否則等待,當信箱不滿時,辯論者才能將新郵件放入信箱,否則等待。請新增必要的訊號量和P,V(或wait,signal)操作,以實現上述過程的同步,要求寫出完整步驟。
思路分析:本題是個雙向生產者消費者問題,同時要求每次要將獲得的東西整理後重新發給對方。
semaphore empty1=N-x,full1=x;
semaphore empty2=M-y,full2=y;
semaphore mutex1=1,mutex2=1;
A{
while(TRUE){
V(full1);
P(mutex1);
從A的信箱中取出一個郵件;
V(mutex1);
P(empty2);
回答問題並提出一個新問題;
P(mutex2);
將新郵件放入B的信箱;
V(mutex2);
}
}
B{
while(TRUE){
V(full2);
P(mutex2);
從B的信箱中取出一個郵件;
V(mutex2);
P(empty1);
回答問題並提出一個新問題;
P(mutex1);
將新郵件放入A的信箱;
V(mutex1);
}
}