操作系統(三)——信號量、死鎖


iwehdio的博客園:https://www.cnblogs.com/iwehdio/

1、信號量

  • 信號量機制:

    • 概念:其實就是一個變量,可以用一個信號量來表示系統中某種資源的數量、

    • 用戶進程通過使用操作系統提供的一對原語來對信號量進行操作,從而方便的實現了進程互斥。

    • 這里的一對原語是指wait(S)和signal(S),也簡寫為P(S)和V(S),即申請和釋放資源。P、V操作必須成對出現。

    • 整數型信號量:

      • 用一個整數作為信號量,數值表示某種資源數。
      • 對信號量的操作只有三種:初始化、P操作、V操作。
      • 不滿足讓權等待原則。
    • 記錄型信號量:

      • S.value表示某種資源數,S.L指向等待該資源的隊列。
      • P操作中,先S.value++,之后可能執行block阻塞原語。
      • V操作中,先S.value--,之后可能執行wakeup喚醒原語。
      • 可以用記錄型信號量實現系統資源的申請和釋放,申請S.value--,然后如果S.value<0說明資源分配完了,就阻塞;釋放S.value++,然后如果S.value<=0說明還有進程在等待隊列中等待,就喚醒。
      • 記錄型信號量可以實現進程互斥、進程同步。

    • 實現進程互斥:

      • 划定臨界區。
      • 設置互斥信號量mytex,初值為1。
      • 在臨界區之前執行P(mutex),在臨界區之后執行V(mutex)。
    • 實現進程同步:

      • 分析那些地方是必須保證一前一后執行的兩個操作。
      • 設置同步信號量S,初始值為0。
      • 在“前操作”之后執行V(S)。
      • 在“后操作”之前執行P(S)。

    • 實現前驅關系:

      • 每一對前驅關系都是一個進程同步問題。
      • 為每一對前驅關系設置一個同步變量,初始值為0。
      • 在“前操作”之后執行V操作。
      • 在“后操作”之前執行P操作。

  • 生產者消費者問題:

    • 生產者每次生產一個產品放入緩沖區,消費者每次從緩沖區取出一個產品使用。緩沖區滿生產者必須等待(同步關系1),緩沖區空消費者必須等待(同步關系2)。

    • 緩沖區是臨界資源,必須被互斥訪問(互斥關系)。

    • 問題中的P、V操作:

      • 生產者每次P一個緩沖區,V一個產品。
      • 消費者每次V一個緩沖區,P一個產品。
      • 生產者和消費者互斥訪問緩沖區。
    • 問題實現:

    • 相鄰的P操作的順序不能改變,否則會出現死鎖。實現互斥的P操作一定要實現同步的P操作之后。相鄰的V操作的順序可以改變。

  • 多生產者多消費者問題:

    • 問題描述:桌子上有一只盤子,每次只能向其中放入一個水果。爸爸專向盤子中放蘋果,媽媽專向盤子中放橘子,兒子專等着吃盤子中的橘子,女兒專等着吃盤子中的蘋果。

      只有盤子空時,爸爸或媽媽才可向盤子中放一個水果。僅當盤子中有自己需要的水果時,兒子或女兒可以從盤子中取出水果。

    • 多是指多種類,即不同的生產者生產不同的產品,不同的消費者消費不同的產品,但是共享緩沖區。

    • 問題分析:

      • 互斥關系:(mutex=1):對緩沖區(盤子)的訪問要互斥地進行。

      • 同步關系:
        1,父親將蘋果放入盤子后,女兒才能取蘋果;

        2,母親將橘子放入盤子后,兒子才能取橘子;

        3,只有盤子為空時,父親或母親才能放入水果。

    • 問題實現:

    • 當緩沖區只有1時,沒有互斥信號量也可以。

  • 吸煙者問題:

    • 問題描述:假設一個系統有三個抽煙者進程和一個供應者進程。每個抽煙者不停地卷煙並抽掉它,但是要卷起並抽掉一支煙,抽煙者需要有三種材料:煙草、紙和膠水。三個抽煙者中,第一個擁有煙草、第二個擁有紙、第三個擁有膠水。

      供應者進程無限地提供三種材料,供應者每次將兩種材料放桌子上,擁有剩下那種材料的抽煙者卷一根煙並抽掉它,並給供應者進程一個信號告訴完成了,供應者就會放另外兩種材料再桌上,這個過程一直重復(讓三個抽煙者輪流地抽煙)。

    • 就是一個可以生產多種產品的單生產者的問題。

    • 問題分析:

      • 互斥關系:桌子是大小為1的緩存區,互斥訪問。

      • 同步關系:

        1,桌上有組合一→第一個抽煙者取走東西;
        2,桌上有組合二→第二個抽煙者取走東西;
        3,桌上有組合三→第三個抽煙者取走東西;
        4,發出完成信號→供應者將下一個組合放到桌上。

    • 問題實現:

  • 讀者寫者問題:

    • 問題描述:有讀者和寫者兩組並發進程,共享一個文件,當兩個或兩個以上的讀進程同時訪問共享數據時不會產生副作用,但若某個寫進程和其他進程(讀進程或寫進程)同時訪問共享數據時則可能導致數據不一致的錯誤。因此要求:

      1,允許多個讀者可以同時對文件執行讀操作;

      2,只允許一個寫者往文件中寫信息;

      3,任一寫者在完成寫操作之前不允許其他讀者或寫者工作;

      4,寫者執行寫操作前,應讓已有的讀者和寫者全部退出。

    • 問題分析:

      • 互斥關系:寫進程-寫進程、寫進程-讀進程。讀進程與讀進程不存在互斥問題。
    • 問題實現:

      • 實現1:

        • 問題:只要讀進程還在讀,寫進程就要一直阻塞,可能會餓死。
      • 實現2:

    • 核心思想:設置了一個count計數器來判斷當前進入的進程是否是第第一個/最后一個讀進程。

    • 如果需要一氣呵成的操作,就應該想到使用互斥信號量。

  • 哲學家進餐問題:

    • 問題描述:一張圓桌上坐着5名哲學家,每兩個哲學家之間的桌上擺一根筷子,桌子的中間是一碗米飯。哲學家們傾注畢生的精力用於思考和進餐,哲學家在思考時,並不影響他人。只有當哲學家飢餓時,才試圖拿起左、右兩根筷子(一根一根地拿起)。

      如果筷子己在他人手上,則需等待。飢餓的哲學家只有同時拿起兩根筷子才可以開始進餐,當進餐完畢后,放下筷子繼續思考。

    • 問題分析:

      • 互斥關系:5位哲學家與左右鄰居對其中間筷子的訪問是互斥關系。
      • 這個問題中只有互斥關系,但與之前遇到的問題不同的事,每個哲學家進程需要同時持有兩個臨界資源才能開始吃飯。如何避免臨界資源分配不當造成的死鎖現象,是哲學家問題的精髓。
    • 如何防止死鎖的發生:

      • 方法1:可以對哲學家進程施加一些限制條件,比如最多允許四個哲學家同時進餐。這樣可以保證至少有一個哲學家是可以拿到左右兩只筷子的。
      • 方法2:要求奇數號哲學家先拿左邊的筷子,然后再拿右邊的筷子,而偶數號哲學家剛好相反。用這種方法可以保證如果相鄰的兩個奇偶號哲學家都想吃飯,那么只會有其中一個可以拿起第一只筷子,另一個會直接阻塞。這就避免了占有一支后再等待另一只的情況。
      • 方法3:僅當一個哲學家左右兩支筷子都可用時才允許他抓起筷子。更准確的說法應該是:各哲學家拿筷子這件事必須互斥的執行。這就保證了即使一個哲學家在拿筷子拿到一半時被阻塞,也不會有別的哲學家會繼續嘗試拿筷子。這樣的話,當前正在吃飯的哲學家放下筷子后,被阻塞的哲學家就可以獲得等待的筷子了。
    • 方法3的問題實現:

  • 管程:

    • 為什么引入管程:復雜PV操作,程序易出錯。管程是一種高級的互斥同步操作。

    • 管程的組成(其實就是一種封裝,類比Java的類):

      1,局部於管程的共享數據結構說明(成員變量);
      2,對該數據結構進行操作的一組過程(成員方法操作成員變量);
      3,對局部於管程的共享數據設置初始值的語句(構造函數初始化成員變量);
      4,管程有一個名字(類名)。

    • 管程的基本特征:

      1,局部於管程的數據只能被局部於管程的過程所訪問(成員變量是private);
      2,一個進程只有通過調用管程內的過程才能進入管程訪問共享數據(成員方法是public);
      3,每次僅允許一個進程在管程內執行某個內部過程(互斥訪問)。

    • 管程解決生產者消費問題:

      • 互斥特性的編譯器負責實現的。
      • 可在管程中設置條件變量及等待/喚醒操作以解決同步問題。可以讓一個進程或線程在條件變量上等待(此時,該進程應先釋放管程的使用權,也就是讓出“入口"”);可以通過喚醒操作將等待在條件變量上的進程或線程喚醒。

2、死鎖

  • 什么是死鎖:在並發環境下,各進程因競爭資源而造成的一種互相等待對方手里的資源,導致各進程都阻塞,無法向前推進的現象。

  • 死鎖、飢餓、死循環的區別:

  • 死鎖產生的必要條件:

    • 互斥條件:只有對必須互斥使用的資源的爭搶才會導致死鎖。
    • 不剝奪條件:進程所獲得的資源在未使用完之前,不能由其他進程強行奪走,只能主動釋放。
    • 請求和保持條件:進程已經保持了至少一個資源,但又提出了新的資源請求,而該資源又被其他進程占有,此時請求進程被阻塞,但又對自己已有的資源保持不放。
    • 循環等待條件:存在一種進程資源的循環等待鏈,鏈中的每一個進程已獲得的資源同時被下一個進程所請求(循環等待不一定死鎖)。
  • 什么時候對發生死鎖:

    • 對系統資源的競爭。
    • 進程推進順序非法。
    • 信號量使用不當。
    • 總之,就是對不可剝奪的資源的不合理分配。
  • 死鎖的處理策略:

    • 預防死鎖,破環四個必要條件。
    • 避免死鎖,用某種方法防止系統進入不安全狀態(銀行家算法)。
    • 死鎖的檢測和解除,允許死鎖的發生,不過操作系統會負責檢測出死鎖的發生,然后采取某種措施解除死鎖。
  • 預防死鎖:

    • 破壞互斥條件:把只能互斥使用的資源改造為允許共享使用,比如SPOOLing技術。
    • 破壞不剝奪條件:
      • 方案1:當某個進程請求新的資源得不到滿足時,它必須立即釋放保持的所有資源,待以后需要時再重新申請。
      • 方案2:當某個進程需要的資源被其他進程所占有的時候,可以由操作系統協助,將想要的資源強行剝奪。
    • 破壞請求和保持條件:可以采用靜態分配方法,即進程在運行前一次申請完它所需要的全部資源,在它的資源未滿足前,不讓它投入運行。一旦投入運行后,這些資源就一直歸它所有,該進程就不會再請求別的任何資源了。
    • 破壞循環等待條件:可采用順序資源分配法。首先給系統中的資源編號,規定每個進程必須按編號遞增的順序請求資源,同類資源(即編號相同的資源)一次申請完。
  • 避免死鎖:

    • 安全序列:指如果系統按照這種序列分配資源,則每個進程都能順利完成。只要能找出一個安全序列,系統就是安全狀態。當然,安全序列可能有多個。

    • 如果系統處於安全狀態,就一定不會發生死鎖。如果系統進入不安全狀態,就可能發生死鎖。

    • “銀行家算法”的核心思想:在進程提出資源申請時,先預判此次分配是否會導致系統進入不安全狀態。如果會進入不安全狀態,就暫時不答應這次請求,讓該進程先阻塞等待。

    • 銀行家算法的步驟:

  • 死鎖的檢測:

    • 用某種數據結構來保存資源的請求和分配信息;
    • 提供一種算法,利用上述信息來檢測系統是否已進入死鎖狀態。
      • 最終能消除所有邊,就稱這個圖是可完全簡化的。此時一定沒有發生死鎖。(相當於能找到一個安全序列)
      • 如果最終不能消除所有邊,那么此時就是發生了死鎖。最終還連着邊的那些進程就是處於死鎖狀態的進程。

  • 死鎖的解除:

    • 資源剝奪法。掛起(暫時放到外存上)某些死鎖進程,並搶占它的資源,將這些資源分配給其他的死鎖進程。
    • 撤銷進程法(或稱終止進程法)。強制撤銷部分、甚至全部死鎖進程,並剝奪這些進程的資源。
    • 進程回退法。讓一個或多個死鎖進程回退到足以避免死鎖的地步。這就要求系統要記錄進程的歷史信息,設置還原點。

iwehdio的博客園:https://www.cnblogs.com/iwehdio/


免責聲明!

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



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