深入探討程序間通訊的重要性:理解不同的通訊機制(下)

2023-09-01 12:00:44

前言

在上一篇文章中,我們探討了程序間通訊的三種常見機制:管道、訊息佇列和共用記憶體。我們瞭解到,這些機制各有其特點和適用場景,可以根據實際需求選擇合適的機制進行程序間通訊。然而,程序間通訊並不僅限於這三種方式。

在本文中,我們將繼續探索程序間通訊的知識點,重點關注號誌、訊號和通訊端。號誌是一種用於程序同步的機制,它可以用於控制對共用資源的存取。訊號是一種用於程序間通知的機制,可以用於處理非同步事件。而通訊端則是一種用於網路通訊的介面,它可以實現不同主機之間的程序間通訊。

號誌

共用記憶體通訊方式雖然提供了高效的資料交換,但也引發了新的問題。如果多個程序同時修改同一個共用記憶體區域,很可能會導致資料衝突。舉個例子,如果兩個程序同時寫入同一個地址,先寫入的程序可能會發現自己的內容被後寫入的程序覆蓋。

在程序間共用資源時,使用號誌可以避免多個程序同時存取共用資源而導致資料衝突的問題。號誌是一個整型計數器,用來表示資源的可用數量。通過P操作和V操作來控制號誌的值。

  • P操作會將號誌減1,如果結果小於0,則表示資源已被佔用,程序需要阻塞等待。如果結果大於等於0,則表示資源仍然可用,程序可以繼續執行。
  • V操作會將號誌加1,如果結果小於等於0,則表示有其他程序正在等待資源,需要喚醒其中一個程序。如果結果大於0,則表示沒有程序在等待資源。

通過使用P操作和V操作,可以實現對共用資源的互斥存取和同步執行。例如,可以初始化一個號誌為1,使得只有一個程序可以存取共用資源,從而避免資料錯亂。另外,可以初始化一個號誌為0,使得程序按照特定的順序執行,實現多程序的同步。
接下來,我們先看下互斥存取,如果要使得兩個程序互斥存取共用記憶體,我們可以初始化號誌為 1。

具體的過程如下:

  • 程序 A 在存取共用記憶體之前,先執行了 P 操作。由於號誌的初始值為 1,所以程序 A 執行 P 操作後,號誌減為 0,表示共用資源可用,程序 A 可以存取共用記憶體。
  • 如果此時程序 B 也想存取共用記憶體,它執行了 P 操作。結果號誌變為 -1,表示臨界資源已被佔用,因此程序 B 被阻塞。
  • 直到程序 A 存取完共用記憶體,才會執行 V 操作,使得號誌恢復為 0。接著,程序 A 喚醒被阻塞的程序 B,使其可以存取共用記憶體。
  • 最後,程序 B 完成共用記憶體的存取後,執行 V 操作,將號誌恢復到初始值 1。

將號誌初始化為 1,代表著它是一個互斥號誌。這種設定可以確保在任何時刻只有一個程序可以存取共用記憶體,從而有效保護了共用記憶體的完整性。有人可能會發現如果多執行緒都來存取資源全部阻塞了喚醒誰呢?這不就是我們之前講到的程序排程演演算法了嗎?程序阻塞後會進入阻塞佇列,而喚醒哪個程序則由系統的排程演演算法決定。

在多程序環境中,每個程序並不一定按照順序執行,它們以各自獨立且不可預測的速度向前推進。然而,在某些情況下,我們希望多個程序能夠密切合作,以實現一個共同的任務。

比如生產者消費者模式,假設程序A負責生產資料,而程序B負責讀取資料,這兩個程序是相互合作、相互依賴的。程序A必須先生產資料,然後程序B才能讀取到資料,因此它們之間存在執行順序。

接下來,我們來討論同步執行。我們可以通過初始化號誌為0來實現。

具體過程如下:

  • 如果程序B比程序A先執行,那麼當它執行P操作時,由於號誌的初始值為0,所以號誌會變為-1,表示程序A還沒有生產資料,程序B會被阻塞等待。
  • 接著,當程序A生產完資料後,執行V操作,號誌會變為0,這會喚醒被阻塞在P操作的程序B。
  • 最後,程序B被喚醒後,意味著程序A已經生產了資料,程序B就可以正常讀取資料了。

可以看出,將號誌初始化為0,代表著這是一個同步號誌,它可以保證程序A在程序B之前執行。

訊號

訊號是一種在異常情況下進行程序間通訊的機制,它是一種非同步通訊方式,其資料結構一般為一個數位。

在Linux作業系統中,為了響應各種事件,提供了幾十種訊號,每個訊號代表著不同的含義。可以通過執行"kill -l"命令來檢視所有的訊號列表。

對於在Shell終端中執行的程序,我們可以通過鍵盤輸入某些組合鍵向程序傳送訊號。例如,按下Ctrl+C會產生SIGINT訊號,表示終止該程序;按下Ctrl+Z會產生SIGTSTP訊號,表示暫停該程序,但程序並未結束。需要注意的是,Ctrl+Z命令在某些情況下可能會導致記憶體飆升等問題(比如你看一個全量伺服器紀錄檔),因此需要謹慎使用。

如果程序在後臺執行,可以使用kill命令向程序傳送訊號,但前提是需要知道正在執行的程序的PID(程序ID)。例如,執行"kill -9 ###"命令會向PID為"###"的程序傳送SIGKILL訊號,用於立即終止該程序。

因此,訊號的事件來源主要有硬體來源(如鍵盤的Ctrl+C)和軟體來源(如kill命令)。訊號是程序間通訊機制中唯一的非同步通訊機制,程序需要為訊號設定相應的監聽處理程式,當收到特定訊號時,執行相應的操作,類似於其他程式語言中的通知機制。

Socket

Socket通訊是一種常用的程序間通訊機制,可以用於跨網路與不同主機上的程序之間通訊,也可以在同一臺主機上的程序之間進行通訊。

Socket通訊是通過網路協定進行資料傳輸的一種方式。在使用Socket通訊時,一個程序可以作為伺服器端建立一個Socket,並指定一個IP地址和埠號來監聽連線請求;另一個程序則可以作為使用者端建立一個Socket,指定伺服器的IP地址和埠號來發起連線。一旦連線建立,伺服器和使用者端就可以通過Socket進行資料的傳送和接收。

在同一臺主機上,程序可以使用特殊的IP地址(如本地迴環地址127.0.0.1)和不同的埠號來建立Socket連線,實現程序間的通訊。這種方式被稱為本地迴環通訊,可以用於程序之間的共同作業和資料交換。

後期我將詳細講解計算機基礎的網路篇,敬請期待!

總結

IPC 機制 資料抽象 參與者 方向 核心實現
管道 位元組流 兩個程序 單向 通常以 FIFO 的緩衝區來管理資料。
有匿名管道和命名管道兩類主要實現
訊息佇列 訊息 多程序 單向
雙向
佇列的組織方式。
通過檔案的許可權來管理對佇列的訪間
號誌 計數器 多程序 單向
雙向
核心餘護共用計數器。
通過檔案的許可權來管理劉計數器的存取
共用記憶體 記憶體區問 多程序 單向
雙向
核心維護共用的記憶體區可。
通過檔案的許可權來管理對共用記憶體的訪間
訊號 事件編號 多程序 單向 為執行緒/程序維護訊號等待佇列。
通過使用者了組等的許可權來管理訊號的操作
通訊端 資料包文 兩個程序 單向
雙向
有基於IP/埠和基於檔案路輕的定址方式。
利用網路棧來管理通訊

程序間通訊是作業系統中的重要概念,它允許不同的程序之間進行資料交換、訊息傳遞和共同作業。在Linux系統中,提供了多種程序間通訊的機制,包括管道、訊息佇列、共用記憶體、號誌、訊號和通訊端。每種通訊機制都有不同的特點和適用場景。需要根據具體需求選擇合適的方式。程序間通訊涉及到資源的共用和競爭,需要合理地使用同步和互斥機制來保證資料的一致性和正確性。同時,程序的喚醒順序也會受到系統的排程演演算法的影響。