操作系統:進程同步


基本概念

在 Os 中引入進程后,雖然提高了資源的利用率和系統的吞吐量,但由於進程的異步性,也會給系統造成混亂,尤其是在他們爭用臨界資源時。例如,當多個進程去爭用一台打印機時,有可能使多個進程的輸出結果交織在一起,難於區分;而當多個進程去爭用共享變量、表格、鏈表時,有可能致使數據處理出錯。進程同步的主要任務是對多個相關進程在
執行次序上進行協調,以使並發執行的諸進程之間能有效地共享資源和相互合作,從而使程序的執行具有可再現性

  • 在資源共享的情況下:保證諸進程以互斥的方式訪問臨界資源必須以互斥方式訪問的共享資源
  • 在相互合作的關系中:進程同步的主要任務是保證相互合作的諸進程在執行次序上協調,(有些教材把這種功能稱做“協調”)。相互合作的進程可能同時存在資源共享的關系。

如何實現進程互斥,需要讓進程以互斥的方式進入各自的臨界區,先執行進入區的代碼。

人為地加一段代碼。

臨界資源

必須以互斥方式訪問的共享資源

counter的例子:在機器語言中實現兩個進程給count加一的操作

register1 = count
register1 = register1 + 1
count = register1
register2 = count
register2 = register2 + 1
count = register2

但是如果是並發執行,可能會出現下面的情況

register1 = count
register2 = count
register1 = register1 + 1
register2 = register2 + 1
count = register1
count = register2

結果就不對了。

可見,counter應該作為臨界資源。多個進程必須對其進行互斥訪問

臨界區

在每個進程中訪問臨界資源的那段代碼稱為臨界區。如果能保證諸進程互斥地進入自己的臨界區,便可實現諸進程對臨界資源的互斥訪問。

每個進程在進入臨界區之前,應先對欲訪問的臨界資源進行檢查,看它是否正被訪問。如果此刻該臨界資源未被訪問,進程便可進入臨界區對該資源進行訪問,並設置它正被訪問的標志;如果此刻該臨界資源正被某進程訪問,則本進程不能進入臨界區。

必須在臨界區前面增加一段用於進行上述檢查的代碼,把這段代碼稱為進入區(entry section)。相應地,在臨界區后面也要加上一段稱為退出區(exit section)的代碼,用於將臨界區正被訪問的標志恢復為未被訪問的標志。

同步機制應遵守的規則

  • 空閑讓進。當無進程處於臨界區時,表明臨界資源處於空閑狀態,應允許一個請求進入臨界區的進程立即進入自己的臨界區,以有效地利用臨界資源。
  • 忙則等待。當已有進程進入臨界區時,表明臨界資源正在被訪問,因而其它試圖進入臨界區的進程必須等待,以保證對臨界資源的互斥訪問。
  • 有限等待。對要求訪問臨界資源的進程,應保證在有限時間內能進入自己的臨界區,以免陷入“死等”狀態。
  • 讓權等待。當進程不能進入自己的臨界區時,應立即釋放處理機,以免進程陷入“忙等”狀態。飢餓狀態。

信號量機制

用某種類型的變量來表示資源包括臨界資源的使用狀態

對不同的臨界資源必須定義不同的信號量

整型信號量

跟臨界資源對應的共享變量——信號量,來標記他的可用數量(臨界值為1)來控制進程等待還是繼續運行

整型信號量:整形變量共享(全局變量),用他的值來標記資源使用情況>0說明有可用資源;定義一個用於互斥的整形信號量,初始化為1

/*整型信號量的wait和signal操作
思路:為必須互斥訪問的Cs定義一個互斥信號量mutex,初始值為1。
然后,將Cs放在 wait(mutex)和signal(mutex)之間,當Cs可訪問時,wait(mutex)才能正常結束使進程進入Cs。
這是利用信號量來實現進程互斥
*/
var s:integer;    ;s為整型信號量
wait(s){            ;用於申請資源
     while s≤0 do no-op;
     s=s-1;
}
;cs
signal(s){          //用於釋放資源
	 s=s+1;
}

wait(s)和 signal(s)是兩個原子操作,因此,它們在執行時是不可中斷的。亦即,當一個進程在修改某信號量時,沒有其他進程可同時對該信號量進行修改。此外,在 wait 操作中,對 s 值的測試和做 s:=s-1 操作時都不可中斷

信號量不是臨界資源,但具有臨界資源的特點,可以被多個進程互斥訪問。對信號量的操作必須是原子性的。(禁止套娃)

為什么不能直接把臨界區定義為原子操作?

內核中臨界區代碼很短,若直接在執行臨界區代碼前關中斷,效率太低

記錄型信號量

在整型信號量機制中的 wait 操作,只要是信號量 s≤0,就會不斷地測試。因此,該機制並未遵循“讓權等待”的准則,而是使進程處於“忙等”的狀態。沒有釋放處理機,沒有放棄對CPU的使用權。

在信號量機制中,除了需要一個用於代表資源數目的整型變量 value 外,還應增加一個進程鏈表指針 L,用於鏈接上述的所有等待進程。記錄型信號量是由於它采用了記錄型的數據結構而得名的。

#記錄型信號量的數據類型 類似結構體
Type  semaphore = record
     Value:integer               //資源數量
     L:list of process           //阻塞隊列
end
/*記錄型信號量的wait(s)和signal(s)操作*/
procedure wait(s)
    var s:semaphore
        begin
            s.value=s.value-1;
            if s.value<0 then  //說明原先的資源數為0
                block(s.L)
        end
procedure signal(s)
    var s:semaphore
        begin
            s.value=s.value+1;    //釋放一個資源
            if s.value <= 0 then  //如果等待隊列里有阻塞的進程,喚醒一個進程 
                wakeup(s.L)      //說明原先的資源數是小於0的,釋放一個之后還等於0,意味着需要喚醒進程
        end

上述代碼如何做到讓權等待:

對它的每次 wait 操作,意味着進程請求一個單位的該類資源,使系統中可供分配的該類資源數減少一個,因此描述為 s.value:=s.value-1;當 s.value<0 時,表示該類資源已分配完畢,因此進程應調用 block 原語,進行自我阻塞放棄處理機,並插入到信號量鏈表s.L 中。

每次signal操作,如果釋放一個資源,資源數還沒有大於0,說明等待隊列里有阻塞的進程。

經典同步問題

生產者消費者模型

生產者進程生產消息,並將消息提供給消費者進程消費。為使生產者進程和消費者進程能並發執行,在它們之間設置了一個具有n個緩沖區的緩沖池,生產者進程可以將它所生產的消息放入一個緩沖區中,消費者進程可以從一個緩沖區中取得一個消息消費。任意兩個進程必須以互斥的方式訪問公共緩沖池。當緩沖池空時,消費者進程必須等待;當緩沖池裝滿消息時,生產者進程必須等待

一共有兩個進程,生產者進程和消費者進程,他們之間是互斥的關系。這道題的臨界資源是緩沖區。必須先生產后消費。

信號量:

  • mutex用於實現對公共緩沖池的互斥,初值為1

  • empty空緩沖區的個數,n

  • full裝有產品的緩沖區數,0

semaphore mutex=1;
semaphore empty=N;
semaphore full=0
//生產者
Producer:
 begin
	repeat
	produce an item in nextp;
	wait(empty);
	wait(mutex);//wait操作不能顛倒,如果有了臨界區的訪問權,空緩沖區數又為空,就會出現死鎖
	buffer(in):=nextp;
	in:=(in+1)mod n
	signal(mutex);
	signal(full);
	until false;
end
//消費者
Consumer:
  begin
     repeat
     wait(full);//申請非空緩沖區
     wait(mutex);//申請公共緩沖池的訪問權
     nextc:=buffer(out);//取出
     out:=(out+1)mod n;
     signal(mutex);
     signal(empty);
     consume item in nextc;//消費數據
     until false; 
end

這是實現相互合作的一個模板

讀者寫者問題

一個數據文件或記錄,可被多個進程共享,我們把只要求讀該文件的進程稱為“Reader進程”,其他進程則稱為“Writer 進程”。允許多個進程同時讀一個共享對象,因為讀操作不會使數據文件混亂。但不允許一個 Writer 進程和其他 Reader 進程或 Writer 進程同時訪問共享對象,因為這種訪問將會引起混亂。

設置信號量:

  • readcount對進入共享區的讀進程計數
  • rmutex用於對多個進程共享的變量readcount互斥
  • wmutex 用於實現讀/寫互斥,寫/寫互斥

要實現寫的時候,讀寫互斥比較簡單

writer:
 begin
  repeat
  wait(wmutex);
  writing operation
  signal(wmutex);
 end

讀進程:

read:
 begin:
  repeat
  wait(rmutex);
  if readcount == 0 then wait(wmutex);//讓第一個讀進程加鎖
  readcount++
  signal(rmutex);
  reading file
  wait(rmutex);
  readcount--;
  if readcount == 0 then signal(wmutex);//讓最后一個讀進程解鎖
  signal(rmutex);
 end

多個讀進程要使readcount+1,所以需要將readcount加鎖。

哲學家就餐

如果保證只有四個人拿筷子,就不會出現死鎖

https://blog.csdn.net/speedme/article/details/17597373

repeat 
wait(mutex);//拿筷子的人的個數
wait(chopstick[i]);
wait(chopstick[(i+1)mod 5]);
eat;
signal(chopstick[i]);
signal(chopstick[(i+1)mod 5]);
signal(mutex);
think;
until false;

練習題

三個進程\(P_1,P_2,P_3\)互斥使用一個包含\(N(N > 0)\)個單元的緩沖區。\(P_1\)每次用produce()生成一個正整數並用put()送入緩沖區某一空單元中;\(P_2\)每次用getodd()從該緩沖區中取出一個奇數並用countodd()統計奇數個數;\(P_3\)每次用geteven()從該緩沖區中取出一個偶數並用counteven()統計偶數個數。請用信號量機制實現這三個進程的同步與互斥活動,並說明所定義信號量的含義。要求用偽代碼描述。

定義信號量,由於三個都是互斥的:

  • mutex三個進程的互斥信號量
  • odd\(P_1\)\(P_3\)同步信號量奇數的個數
  • even\(P_1\)\(P2\)同步信號量偶數的個數
  • empty同步信號量空緩沖區的個數
semaphore mutex=1;
semaphore odd=0,even=0;
semaphore empty=N;
main()
cobegin{
Process P1
while(True){
    number=produce();
	wait(empty);
    wait(mutex);
	put();
	signal(mutex);
	if number%2 == 0
	signal(even);
    else 
	signal(odd);
}
Process P2
while (true){
      wait(odd);
      wait(mutex);
	  getodd();
	  signal(mutex);
      signal(empty);
 	  countodd();
}
Process P3
while (true){
     wait(even);
     wait(mutex);
	 geteven();
     signal(mutex);
     signal(empty);
     counteven();
}
}coend


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM