Linux進程間通信(IPC)編程實踐(0) 進程間通信簡述和PV原語

 

1 進程間通信

 

進程間通信至少可以通過傳送打開文件來實現,不同的進程通過一個或多個文件來傳遞信息,事實上,在很多應用系統裏,都使用了這種方法。但一般說來, 進程間通信(IPC:InterProcess Communication)不包括這種似乎比較低級的通信方法。Unix系統中實現進程間通信的方法很多,而且不幸的是,極少方法能在所有的Unix系 統中進行移植(唯一一種是半雙工的管道,這也是最原始的一種通信方式)。而Linux作爲一種新興的操作系統,幾乎支持所有的Unix下常用的進程間通信 方法:管道、消息隊列、共享內存、信號量、套接口等等。

 

 

 

 

 

2 進程的同步與互斥

(1) 進程同步: 多個進程需要相互配合共同完成一項任務。

(2) 進程互斥: 由於各進程要求共享資源,而且有些資源需要互斥使用,因此各進程間競爭使用這些資源,進程的這種關係爲進程的互斥;系統中某些資源一次只允許一個進程使用,稱這樣的資源爲臨界資源或互斥資源, 而在進程中涉及到互斥資源的程序段叫臨界區.

對於多個進程的併發狀態,進程是基於CPU時間片輪轉的。

我們可以從下面司機和售票員的關係理解進程間的同步:


司機和售票員是合作的關係,售票員先關門,然後司機啓動車輛,正常運行時間售票,然後到站停車,售票員開門,這是進程間同步的例子。

我們可以利用後面提到的信號量解決進程間的同步和互斥的問題。

 

3 進程間通信的目的

(1) 數據傳輸:一個進程需要將它的數據發送給另外一個進程。
(2) 資源共享:多個進程之間共享同樣的資源。
(3) 進程事件:一個進程需要向另一個或一組進程發送消息,通知他(他們)發生了某種事件(如進程終止時要通知父進程)。
(4) 進程控制:有些進程希望完全控制另一個進程的執行(如Debug進程),此時控制進程希望能夠攔截另一個進程的所有陷入和異常,並能夠及時知道它的狀態改變(可使用SIGTRAP信號實現)。

在進程間通信的發展中:管道->System V(系統基本都支持)->POSIX進程間通信,其中System V使用的很廣泛。


4 進程間通信分類

注意:進程間通信是通過操作系統,而線程間通信是基於進程的,不需要經過操作系統。 

(1) 文件
(2) 文件鎖

(3) 管道(pipe), 管道可用於具有親緣關係進程間的通信,允許一個進程和另外一個與它有共同祖先的進程之間進行通信。

(4) 命名管道(FIFO), 命名管道克服了管道沒有名字的限制,因此,除具有管道所具有的功能外,它還允許無親緣關係進程間通信,命名管道在文件系統中有對應的文件名。命名管道通過命令mkfifo或系統調用mkfifo來創建。 

(5) 信號, 信號是比較複雜的通信方式,用於通知接收進程有某種事件發生,除了用於進程間通信外,進程還可以發送信號給進程本身。

(6) 套接字(socket), 套接字(socket)更爲一般的進程間通信機制,可用於不同機器之間的進程通信。

(7) System V IPC
    System V 消息隊列, System V 共享內存, System V 信號量
     
(8) POSIX IPC
    消息隊列, 共享內存, 信號量, 互斥量, 條件變量, 讀寫鎖
    
 消息隊列是消息的連接表,包括Posix消息隊列、systern V消息隊列。有足夠權限的進程可以向隊列中添加消息,被賦予讀權限的進程則可以讀隊列中的消息、消息隊列克服了信號承載信息量少,管道只能承載無格式字節流以及緩存區大小受限等缺陷。
 
 共享內存:使得多個進程可以訪問同一塊內存空間,是最快可用IPC形式。是針對其他通信機制運行效率較低而設計的。往往與其他通信機制,如與信號量結合使用,來達到進程間的同步及互斥。

 內存映射;內存映射允許多個進程間通信,每一個使用該機制的進程通過把一個共享的文件映射到自己的進程地址空間來實現它。
 
 信號量:主要作爲進程間以及同一進程不同線程之間的同步手段。
  


4.1 進程間共享信息的三種方式

 

4.2 IPC對象的持續性

 

(1) 隨進程持續:一直存在直到打開的最後一個進程結束。(如pipe和FIFO(進程結束,數據刪除))

(2) 隨內核持續:一直存在直到內核自舉(重啓)或顯式刪除(如System V消息隊列、共享內存、信號量)

(3) 隨文件系統持續:一直存在直到顯式刪除,即使內核自舉還存在。(POSIX消息隊列、共享內存、信號量如果是使用映射文件來實現)  

 

 

5 進程死鎖及處理

死鎖是指多個進程之間相互等待對方的資源,而在得到對方資源之前又不釋放自己的資源,這樣,造成循環等待的一種現象。如果所有進程都在等待一個不可能發生的事,則進程就死鎖了。

死鎖產生的四個必要條件

(1) 互斥條件

    進程對資源進行排它性使用,即在一段時間內某資源僅爲一個進程所佔用。 

(2) 請求和保持條件

    當進程因請求資源而阻塞時,對已獲得的資源保持不放。 

(3) 不可剝奪條件

    進程已獲得的資源在未使用完之前,不能被剝奪,只能在使用完時由自己釋放。 

(4) 環路等待條件

    各個進程組成封閉的環形鏈,每個進程都等待下一個進程所佔用的資源

 

5.1 死鎖預防

上面四個條件缺一不可,只要我們隨意破壞2,3,4就可以避免死鎖。

(1) 資源一次性分配:(破壞請求和保持條件)
(2) 可剝奪資源:破壞不可剝奪條件)
(3) 資源有序分配法:(破壞循環等待條件)

      

5.2 死鎖避免

但是上面預防死鎖的幾種策略,會嚴重地損害系統性能。因此在避免死鎖時,要施加較弱的限制,從而獲得較滿意的系統性能。

由於在避免死鎖的策略中,允許進程動態地申請資源。因而,系統在進行資源分配之前預先計算資源分配的安全性。若此次分配不會導致系統進入不安全狀態,則將資源分配給進程;否則,進程等待。其中最具有代表性的避免死鎖算法是銀行家算法。

 

5.2.1 銀行家算法

爲保證資金的安全,銀行家規定: 

(1) 當一個顧客對資金的最大需求量不超過銀行家現有的資金時就可接納該顧客; 

(2) 顧客可以分期貸款,但貸款的總數不能超過最大需求量

(3) 當銀行家現有的資金不能滿足顧客尚需的貸款數額時,對顧客的貸款可推遲支付,但總能使顧客在有限的時間裏得到貸款

(4) 當顧客得到所需的全部資金後,一定能在有限的時間裏歸還所有的資金. 

 

5.2.2 哲學家就餐問題

五個哲學家圍在一個圓桌就餐,每個人都必須拿起兩把叉子才能用餐;

哲學家就餐問題解法:

(1)服務生解法: 將服務生看作是一個管理者, 哲學家在拿叉子之前需要徵得服務生的同意;

(2)最多4個哲學家;

(3)僅當一個哲學家兩邊筷子都可用時才允許他拿筷子;

(4)給所有哲學家編號,奇數號的哲學家必須首先拿左邊的筷子,偶數號的哲學家則反之;

 

 

6 信號量


信號量和P、V原語由Dijkstra(迪傑斯特拉)提出, 迪傑斯特拉的三大貢獻: goto有害, PV原語, 迪傑斯塔拉最短路算法; 

信號量值含義

   S>0:S表示可用資源的個數

   S=0:表示無可用資源,無等待進程

   S<0:|S|表示等待隊列中進程個數

下面代碼是原子性的,硬件中通過關閉中斷來實現,軟件通過關閉中斷實現。   

 

// 信號量定義  
typedef struct{  
   int value;  
   struct process_control_block *list;  
}semaphore;  

 

 

PV操作:

 

// P原語  
// P(semaphore *S)  
wait(semaphore *S)  
{  
    -- S->value;  
    if (S->value < 0)  
    {  
        // 將當前進程設置爲阻塞狀態  
        // 將當前進程的PCB插入相應的阻塞隊列S->list末尾  
        block(S->list);  
    }  
} 

 

// V原語  
// V(semaphore *S)  
signal(semaphore *S)  
{  
    ++ S->value;  
    if (S->value <= 0)  // 表示有進程處於阻塞狀態  
    {  
        // 喚醒阻塞隊列S->list中等待的一個進程,將其置爲就緒態;  
        // 將其插入就緒隊列;  
        wakeup (S->list);  
    }  
}  

 

 

 

使用PV原語解決開篇提到的問題:

 

 

注意:

PV 原語分佈在不同的進程中一般是爲了解決同步問題的,在同一進程中一般是解決互斥的問題。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

本文轉自:

https://blog.csdn.net/NK_test/article/details/49475379

 

 

 

 

 

 

 

 

 

 

 

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