程序間通訊 管道

2021-09-21 07:34:32 字數 4678 閱讀 8351

訊號也是程序間通訊的一種機制,儘管其主要作用不是這個。但不建議將訊號作為程序間通訊的常規手段。

執行緒在linux中被實現為輕量級的程序,執行緒之間的同步手段(互斥量和條件等待),本質也是程序間的通訊。

程序間通訊的手段,大體可以分成以下兩類:

第一類是通訊類。這類手段的作用是在程序之間傳遞訊息,交換資料。若細分下來,通訊類也可以分為兩種,一種是用來傳遞訊息的(比如訊息佇列),另外一種是通過共享一片記憶體區域來完成資訊交換的(比如共享記憶體)。

第二類是同步類。這類手段的目的是協調程序間的操作。某些操作,多個程序不能同時執行,否則可能會產生錯誤的結果,這就需要同步類的工具來協調。

管道是乙個廣泛應用的程序間通訊手段。日常在終端執行shell命令時,會大量用到管道。但管道的缺陷在於只能在有親緣關係(有共同的祖先)的程序之間使用。為了突破這個限制,後來引入了命名管道。

管道的作用就是在有親緣關係的程序之間傳遞訊息。所謂有親緣關係,是指有乙個共同的祖先。所以管道也並非只能用於父子程序之間,也可以用在兄弟程序之間甚至等等。總而言之,只要共同的祖先曾經呼叫了pipe函式,開啟的管道檔案就會在fork之後,被各個後代程序所共享。開啟的管道檔案,只有家族成員知道和具有使用權利。

管道的特性,這個特性是指讀取管道內容是消耗型的行為,即乙個程序讀取了管道內的一些內容之後,這些內容就不會再管道中了。一般來講管道是單向的。乙個程序負責往管道裡面寫內容,另乙個程序讀取管道裡的內容。如果兩個程序之間想雙向通訊怎麼辦?可以建立兩個管道唄。

管道是一種檔案,可以呼叫read、write和close等操作檔案的介面來操作管道。另一方面管道又不是一種普通的檔案,它屬於一種獨特的檔案系統:pipefs。管道的本質是核心維護了一塊緩衝區與管道檔案相關聯,對管道檔案的操作,被核心轉換成對這塊緩衝區記憶體的操作。

在linux下可以使用如下的介面建立管道:

#include int pipe(int pipefd[2]);
如果成功,則返回值是0,如果失敗,則返回值是-1,並且設定errno。

成功呼叫匹配函式之後,會返回兩個開啟的檔案描述符,乙個是管道的讀取端描述符pipefd[0],另乙個是管道的寫入段描述符匹配負擔[1]。管道沒有檔名與之關聯,因此程式沒有選擇,只能通過檔案描述符來訪問管道,只有那些能看到這兩個檔案描述符的程序才能夠使用管道。也就是說,只有該程序及該程序的子孫程序才能看到。

從核心的角度看,呼叫pipe之後,系統給程序分配了兩個檔案描述符,呼叫fork之後,子程序也就有了與管道對應的兩個檔案描述符。和普通檔案不同,這兩個檔案描述符對應的是一塊記憶體緩衝區域。

從中可以看出,任何兩個有親緣關係的程序,只要共同的祖先開啟了乙個管道,總能通過關閉不相關程序的某些管道檔案描述符,來建立起兩者之間單向通訊的管道。

前面提到過,用管道通訊的兩個程序,各持有乙個管道檔案描

述符,不相干的程序應自覺關閉掉這些檔案描述符。這麼做不僅僅

是為了讓資料的流向更加清晰,也不僅僅是為了節省檔案描述符,

更重要的原因是:關閉未使用的管道檔案描述符對管道的正確使用

影響重大。

管道本質是一片記憶體區域,自然有大小。自從linux 2.6.11版本起,管道的預設大小是65536位元組,可以呼叫fcntl來獲取和修改這個值的大小,**如下:

獲取管道的大小

pipe_capacity = fcntl(fd,?f_getpipe_sz);設定管道的大小

ret = fcntl(fd,?f_setpipe_sz, size);

管道記憶體區域的大小必須在頁面大小(page)和上限值之間,其上限記錄在/proc/sys/fs/pipe-max-size裡,對於特權使用者,還可以修改上限值。

cat /proc/sys/fs/pipe-max-size

1048576

管道的容量可以擴大,自然也可以縮小。縮小管道容量時會遇到一種比較有意思的場景,即當前管道中已存在的內容大於fcntl函式呼叫中指定的size,此時fcntl函式會返回失敗

在使用管道的過程中要意識到:管道有大小,寫入須謹慎,不能連續地寫入大量的內容,一旦管道滿了,寫入就會被阻塞;對於讀取端,要及時地讀取,防止管道被寫滿,造成寫入阻塞。

shell程式設計會大量使用管道,我們經常看到前乙個命令的標準輸出作為後乙個命令的標準輸入,來協作完成任務。那麼管道是如何做到呢?

兄弟程序可以通過管道來傳遞資訊,這並不稀奇。關鍵是如何使得乙個程式的標準輸出被重定向到管道中,而另乙個程式的標準輸入從管道中讀取呢?

答案就是複製檔案描述符。

對於第乙個子程序,執行dup2之後,標準輸出對應的檔案描述符1,也成為了管道的寫入端。這時候,管道就有了兩個寫入端,需要關閉不相干的寫入端,使讀取端可以順利地讀取到eof,所以應將剛開始分配的管道寫入端的檔案描述符pipefd[1]關閉掉。同樣的道理,對於第二個子程序,如法炮製。

管道的乙個重要作用是和外部命令進行通行。在日常程式設計中,經常會需要呼叫乙個外部命令,並且需要獲取命令的輸出。而有些時候,需要給外部命令提供一些內容,讓外部命令處理這些輸入。linux提供了popen介面來幫助程式設計師做這些事情。

簡單的說就是通過匹配函式和dup2等函式,也能完成popen

無名管道因為沒有實體檔案與之關聯,靠的是世代相傳的檔案描述符,所以只能應用在有共同祖先的各個程序之間。對於沒有親緣關係的任意兩個程序之間無名管道就愛莫能助了。

命名管道就是為了解決無名管道的這個問題而引入的。fifo與管道類似,最大的差別就是有實體檔案與之關聯。由於存在實體檔案,不相關的沒有親緣關係的程序也可以通過使用fifo來實現程序之間的通訊。

與無名管道相比,命名管道僅僅是批了一件馬甲,其核心與無名管道是一模一樣的。

建立命名管道的介面定義如下:

#include #include int mkfifo(const char *pathname, mode_t mode);
其中,第二個引數的含義是fifo檔案的讀寫執行權利,和open函式類似。當然真實的讀寫執行許可權,還需要按照當前程序umask來取掩碼

在shell程式設計中可以使用-p file來判斷是否為fifo檔案。在c語言中如何判斷是否為fifo檔案呢?通過s_isfifo哄可以判斷,不過要先通過stat或fstat函式來獲取到檔案的屬性資訊,如下:

#include #include #include int stat(const char *path, struct stat *buf);

int fstat(int fd, struct stat *buf);

s_isfifo(buf->st_mode)

一旦fifo檔案建立好了,就可以把它用於程序間的通訊了。一般的檔案操作函式如open、read、write、close、unlink等都可以用在fifo檔案上。

fifo檔案和普通檔案相比,有乙個明顯的不同:程式不應該以

o_rdwr模式開啟fifo檔案。posix標準規定,以o_rdwr模式

開啟fifo檔案,結果是未定義的。當然了,linux提供了對

o_rdwr的支援,在某些場景下,o_rdwr模式的開啟是有價值

的(9.4節給出了乙個例子)。

對fifo檔案推薦的使用方法是,兩個程序乙個以唯讀模式

(o_rdonly)開啟fifo檔案,另乙個以只寫模式(o_wronly)打

開fifo檔案。這樣負責寫入的程序寫入fifo的內容就可以被負責

讀取的程序讀到,從而達到通訊的目的。

開啟乙個fifo檔案和開啟普通檔案相比,又有不同。在沒有進

程以寫模式(o_rdwr或o_wronly)開啟fifo檔案的情況下,以

o_rdonly模式開啟乙個fifo檔案時,呼叫程序會陷入阻塞,直

到另一程序以o_wrony(或者o_rdwr)的標誌位開啟該fifo文

件為止。同樣的道理,在沒有程序以讀模式(o_rdonly或

o_rdwr)開啟fifo檔案的情況下,如果乙個程序以o_wronly

的標誌位開啟乙個fifo檔案,呼叫程序也會阻塞,直到另乙個程序

以o_rdonly(或者o_rdwr)的標誌位開啟該fifo檔案為止。也

就是說,開啟fifo檔案會同步讀取程序和寫入程序。

乍看之下,o_rdonly模式開啟不能返回,在等寫開啟,同樣

o_wronly開啟不能返回,在等讀開啟,造成死鎖,誰都返回不

了。事實上不是這樣的。當o_rdonly開啟和o_wronly開啟的

請求都到達fifo檔案時,兩者就都能返回了。

核心之中,維護有引用計數r_counter和w_counter,分別記錄

fifo檔案兩種開啟模式的引用計數。對於fifo檔案,無論是讀開啟

還是寫開啟,都會根據引用計數判斷對方是否存在,進而決定後續

的行為(是阻塞、返回成功,還是返回失敗)。

fifo檔案提供了o_nonblock標誌位,該標誌位會顯著影響

open的行為模式。將o_rdonly、o_wronly及o_nonblock三

種標誌位結合在一起考慮

程序間通訊 管道

include int pipe int fd 2 返回值 若成功,返回0,若出錯,返回 1經由引數fd返回兩個檔案描述符 fd 0 為讀而開啟,fd 1 為寫而開啟。fd 1 的輸出是fd 0 的輸入。else if pid 0 子程序 else else if pid 0 父程序 printf ...

程序間通訊 管道

原文 程序間通訊 管道 管道簡介 常說的管道都是匿名半雙工管道,匿名半雙工管道在系統中是沒有實名的,並不可以在檔案系統中以任何方式看到該管道。它只是程序的一種資源,會隨著程序的結束而被系統清除。管道通訊是在unix系統中應用比較頻繁的一種方式,例如使用grep查詢 ls grep ipc 顧名思義,...

程序間通訊 管道

程序間通訊,又稱為ipc,包含以下型別 半雙工管道fifo 全雙工管道 訊息佇列 訊號 訊號量共享記憶體 套接字socket streams。一,管道是unix系統ipc的最古老形式,他具有兩種侷限性 1 資料只能在乙個方向上流動 2 只能在具有公共祖先的程序之間使用。乙個管道由乙個程序建立,然後該...