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

2023-08-31 12:01:40

程序間通訊

在作業系統中,程序間通訊是指不同程序之間進行資訊共用、資料傳輸和訊息通知等互動的過程。每個程序在建立時都有自己獨立的虛擬地址空間,但它們共用核心空間。因此,要實現程序間的通訊,必須通過核心來進行中介,如下圖所示:

在Linux系統中,提供了多種程序間通訊的機制,包括管道、訊息佇列、共用記憶體、號誌、訊號、通訊端等。這些機制允許程序之間共用資料、傳輸訊息以及進行程序間的同步與通訊。下面我們詳細講解下。

管道

管道是一種程序間通訊機制,它可以將一個程序的輸出直接作為另一個程序的輸入。在Linux系統中,管道可以用於將命令的輸出傳遞給另一個命令進行處理。

ps -ef |grep java

使用Linux系統練手的時候,想必大家都是用這樣的一種命令檢視java程序,命令中的 | 就是管道命令,但是這個是匿名管道,用完了就銷燬,匿名管道只能在有父子關係的程序之間進行通訊。他的功能也很好理解,一個程序的輸出直接作為另一個程序的輸入,所以才能只展現java程序,所以他的傳輸方式是單向傳輸。

那麼既然有匿名管道,就有命名管道,被叫做 FIFO,因為資料是先進先出的傳輸方式。命名管道具有讀寫兩個埠,程序可以通過開啟管道的檔案來進行讀取或寫入。當一個程序寫入資料到管道時,另一個程序可以從管道中讀取資料。

在使用命名管道前,先需要通過 mkfifo 命令來建立,並且指定管道名字:

$ mkfifo myPipe

myPipe 是管道的名稱,在 Linux 中一切皆檔案的原則下,管道也以檔案的形式存在。我們可以使用 ll 命令檢視一下,該檔案的型別是 p,表示為管道(pipe)。

接下來,我們將資料寫入名為 myPipe 的管道中:

在執行完寫入操作後,你可能會發現命令執行後一直停留在那裡。這是因為管道中的資料沒有被讀取,只有當管道中的資料被完全讀取後,命令才能正常退出。因此,我們需要執行另一個命令來讀取管道中的資料:

可以觀察到,管道中的內容已經被成功讀取並列印在終端上,另外,echo命令也正常退出了。

從中我們可以得知,匿名管道的通訊範圍限定在具有父子關係的程序之間。由於管道本身沒有實體,也就是沒有管道檔案,所以只能通過fork來複制父程序的檔案描述符,以實現程序間的通訊。(fork是一個作業系統呼叫,用於建立一個新的程序。當呼叫fork時,作業系統會複製當前程序的副本)

在shell中執行A | B命令時,A程序和B程序都是由shell建立的子程序。A和B之間不存在父子關係,它們的父程序都是shell。

此外,對於命名管道,它可以在不相關的程序之間進行通訊。這是因為命名管道事先建立了一個特定型別的裝置檔案,在程序中只需要使用該裝置檔案,就可以實現程序之間的通訊。

訊息佇列

訊息佇列是一種程序間通訊的機制,它相比於管道具有更高的效率和靈活性。訊息佇列是通過在核心中建立一個訊息連結串列來實現的,程序可以將資料放入訊息佇列中,然後其他程序可以從佇列中讀取這些資料。

例如,當程序A需要向程序B傳送訊息時,程序A將資料放入B程序對應的訊息佇列後即可正常返回。而程序B可以在需要時去讀取資料。同樣地,當程序B需要向程序A傳送訊息時,也可以按照相同的方式進行操作。

與管道不同的是,訊息佇列是有格式的,每個訊息體都是固定大小的儲存塊,程序在讀取資料時需要約定好訊息體的資料型別。訊息佇列的優勢在於可以支援程序間的非同步通訊,傳送方和接收方不需要同時執行,訊息可以在佇列中等待對方讀取。不像管道是無格式的位元組流資料。如果程序從訊息佇列中讀取了訊息體,核心就會把這個訊息體刪除。

訊息佇列的生命週期與核心相關,如果沒有顯式地釋放訊息佇列或關閉作業系統,訊息佇列將一直存在。而管道的生命週期是隨著程序的建立和結束而動態建立和銷燬。

然而,訊息佇列也存在一些缺點。由於資料在使用者態和核心態之間進行拷貝,訊息佇列通訊過程中存在一定的開銷。當程序將資料寫入訊息佇列時,需要將資料從使用者態拷貝到核心態;而另一個程序從訊息佇列中讀取資料時,需要將資料從核心態拷貝到使用者態。這種資料拷貝開銷會影響通訊的效率。

共用記憶體

共用記憶體是一種高效的程序間通訊機制,它允許多個程序共用同一塊記憶體區域,避免了資料的拷貝過程,提高了通訊速度。

在共用記憶體機制中,作業系統將一塊共用記憶體區域對映到多個程序的虛擬地址空間中,使得它們可以直接存取同一塊實體記憶體。這樣,一個程序對共用記憶體的寫入操作,其他程序可以立即看到更新後的資料,而不需要進行資料的拷貝傳輸。

由於共用記憶體不進行資料拷貝,因此在程序間通訊的過程中,它具有較低的開銷和較高的傳輸速度。然而,共用記憶體機制需要通過同步機制來保證多個程序之間的資料一致性,以免出現競爭條件和資料不一致的問題。

總結

本篇文章總結了程序間通訊的三種常見機制:管道、訊息佇列和共用記憶體。它介紹了每種機制的特點、優缺點以及適用場景。管道適用於父子程序之間的通訊,但只能在有親緣關係的程序之間使用。訊息佇列可以用於非同步通訊,並且支援多個程序之間的通訊,但是訊息的格式需要事先定義。共用記憶體是一種高效的通訊方式,可以實現多個程序共用同一塊記憶體區域,但需要處理程序間的同步和互斥。根據實際需求,可以選擇合適的機制進行程序間通訊。

下一篇文章將繼續探討號誌、訊號和通訊端的知識點!