【Linux】Linux的同步控制

在多任務系統中,通常會有多個進程以併發的方法在運行,這種併發活動與單任務系統相比有着截然不同的一些特徵:

  • 由於進程何時運行、何時中止不由程序設計者來掌握,而是由調度器來決定,即併發活動的隨機性;
  • 由於系統資源數量的限制,必然會使多個需要同時使用同一資源的進程發生衝突,即併發活動的競爭性;
  • 由於共享資源的存在,使得進程在共享資源上的活動能夠被其它進程瞭解和干涉,即併發活動的開放性;
  • 當多個進程需要共同配合來完成一項工作時,必然要產生相互的制約,即併發活動的制約性;
  • 爲協同工作,各個進程之間要有必然的交互,即併發活動具有交互性。

其實,仔細分析一下就會知道,引起進程之間產生各種複雜關係的原因主要就是兩個:一是共享資源的存在;二是因完成同一項任務而需要協作。共享資源的存在導致了進程的競爭,協作的需要導致了同步。所以,操作系統必須提供相應的手段對競爭進行必要的控制,同時還要爲進程提供必要的同步手段。

現代操作系統用於進程競爭控制的有效手段是信號量以及信號量集。

 

競爭、臨界區與互斥

在多任務系統中,當進程因使用共享資源而發生競爭時,操作系統必須對競爭進行必要的控制,以使進程可以有序地使用共享資源而不發生錯誤。

進程競爭

本來,進程不記錄其它進程的任何情況,並且有自己的私有運行空間,它們之間各自獨立、彼此無關。但遺憾的是,它們總是可能需要使用打印機等共享資源,那麼當多個進程在同一個時間要使用同一個共享資源時,就一定會出現爭搶現象,這種現象就叫做進程的競爭。

例如:進程A、B都需要使用打印機,進程A先獲取使用權,但在其未打印完成就因其時間片已到,調度器將打印機的使用權分配給了進程B。那麼就會將B需要打印的內容打印到A未完成的後面,顯然,這是錯誤的!

可以這麼認爲,共享資源是“危險物”,而與共享資源有關的這些程序段就是程序中事故高發段——“危險段”。當兩個以上的“危險段”併發時,就可能會發生嚴重事故。

臨界段

在計算機技術中,將程序中與共享資源有關的危險程序段稱爲“臨界段”,而共享資源叫做“臨界資源”,這是兩個由Dijkstra在1965年提出並沿用至今的概念。

仔細分析一下就會知道,增加資源以避免競爭時解決上述問題最簡單的方法。但在計算機系統往往由於成本原因不可能這麼辦,那麼剩下的就只剩下一個方法:杜絕不同進程臨界段的同時執行。

具體方法爲:使程序中的臨界段爲原子操作,即臨界段的執行不被任何其他代碼所中斷;也就是說,令多個進程中與同一個共享資源相關的臨界段在執行上是互相排斥的,簡稱“互斥”。

在這裏附帶說一下,操作系統中還有很多處於某種原因(不一定與共享資源相關)必須連續運行而不允許中斷的操作代碼段,這種代碼段叫做“原語”。

 

信號量與P、V操作

在臨界段佔用共享資源期間,使該資源暫時成爲該臨界段的獨佔資源是實現互斥的核心思想。

信號量的基本概念

在前面學習UCOSIII時已經知道,用只有兩個值的信號量可以很好地解決互斥問題,人們也常常把這種信號量叫做互斥型信號量。下圖是兩個進程使用互斥型信號量無衝突的訪問一個共享資源的示意圖:

進程1在訪問共享資源之前先進行請求信號量的操作,當進程1發現信號量的標誌位1時,它一方面把信號量的標誌由1改爲0,另一方面進行共享資源的訪問。如果進程2在進程1已經獲得信號量之後再請求信號量,那麼因爲它獲得的標誌值爲0,所以進程1就只能等待而不能訪問共享資源了。顯然,這種做法可以有效地防止兩個進程同時訪問同一個共享資源所造成的衝突。

那麼進程2何時可以訪問共享資源呢?當然是在進程1使用完共享資源之後,由進程1向信號量發信號使信號量標誌的值由0再變成1時,進程2就有機會訪問共享資源了。同進程1一樣,進程2一旦獲得了共享資源的訪問權,那麼在訪問共享資源之前一定要把信號量標誌的值由1變成0。

信號量的P和V操作

從前面敘述可知,表示共享資源被佔用情況標誌可以是一個整型變量s,除了初始化外,該變量僅能通過兩個由進程使用的操作原語P和V來改變它。其中P叫做測試原語(Problem),V叫做增量原語(Verhogen)。

如果把對s的P操作記爲P(s),對s的V操作記爲V(s),那麼,當進程使用P(s)時,P(s)將產生如下動作:

  • 若s大於0時,把信號量s減1,該進程繼續運行;
  • 否則進程進入等待狀態,直至其他進程對s進行V(s)操作,使s大於0爲止。
  • 當進程使用V(s)時,V(s)將產生如下動作:把變量s加1,即s=s+1。

信號量的基本組成

綜上所述,組成信號量的基本要素爲:一個整型變量(標誌)、一組操作(其中必須包含P和V兩個操作)和一個隊列(等待進程隊列)。

其中,等待隊列的作用就是使暫時未獲得允許而處於等待狀態的進程將自己的PCB加入等待隊列,而進程本身則轉入阻塞狀態。

 

臨界段代碼格式

如果把信號量用於互斥,那麼信號量的初值通常爲1,因爲很少有共享資源在系統一開機就被佔用的。這樣在臨界段的前面使用P操作,而在臨界段末尾使用V操作。

例如:設置一個整數變量m作爲資源佔用情況標誌,並分別定義操作P(m)和V(m),那麼進程的臨界段代碼就可以按照下面的形式來進行編寫:

{
        P(m)
                臨界段
        V(m)
}

其中,m的初值爲1。

競爭來源於進程之間的間接制約,形成這個間接制約的中介物就是共享資源,信號量則使共享該那個資源變成暫時的獨佔資源,從而解決了進程因競爭而產生的各種問題。

 

用信號量實現同步

如果有緩衝區如下圖,進程A負責向緩衝區寫數據,進程B負責從緩衝區讀取進程A寫入的數據,那麼進程A就是生產者,進程B就是消費者,而緩衝區就是兩個進程都要使用的共享資源。

顯然,這兩個進程與緩衝區相關代碼的執行順序應該是進程A在前,進程B在後,否則就會出現錯誤。

爲保證正確地執行時序,可以設置兩個信號量s1和s2。其中,s1表示緩衝區是否爲空(0表示非空,1表示空),初值爲1;s2表示緩衝區是否爲滿(0表示非滿,1表示滿),初值爲0。在進程A和進程B中與緩衝區相關的代碼爲:

進程A(生產者)的代碼:

while(1) {
        P(s1)
                對共享資源寫操作
        V(s2)
}

進程B(消費者)的代碼:

while(1) {
        P(s2)
                對共享資源讀操作
        V(s1)
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章