Linux程序間通訊 使用匿名管道

2021-08-27 14:29:12 字數 4448 閱讀 9686

在前面,介紹了一種程序間的通訊方式:使用訊號,我們建立通知事件,並通過它引起響應,但傳遞的資訊只是乙個訊號值。這裡將介紹另一種程序間通訊的方式——匿名管道,通過它程序間可以交換更多有用的資料。

一、什麼是管道

如果你使用過linux的命令,那麼對於管道這個名詞你一定不會感覺到陌生,因為我們通常通過符號「|"來使用管道,但是管理的真正定義是什麼呢?管道是乙個程序連線資料流到另乙個程序的通道,它通常是用作把乙個程序的輸出通過管道連線到另乙個程序的輸入。

舉個例子,在shell中輸入命令:ls -l | grep string,我們知道ls命令(其實也是乙個程序)會把當前目錄中的檔案都列出來,但是它不會直接輸出,而是把本來要輸出到螢幕上的資料通過管道輸出到grep這個程序中,作為grep這個程序的輸入,然後這個程序對輸入的資訊進行篩選,把存在string的資訊的字串(以行為單位)列印在螢幕上。

二、使用popen函式

1、popen函式和pclose函式介紹

有靜就有動,有開就有關,與此相同,與popen函式相對應的函式是pclose函式,它們的原型如下:

#include file* popen (const char *command, const char *open_mode);

int pclose(file *stream_to_close);

poen函式允許乙個程式將另乙個程式作為新程序來啟動,並可以傳遞資料給它或者通過它接收資料。command是要執行的程式名和相應的引數。open_mode只能是"r(唯讀)"和"w(只寫)"的其中之一。注意,popen函式的返回值是乙個file型別的指標,而linux把一切都視為檔案,也就是說我們可以使用stdio i/o庫中的檔案處理函式來對其進行操作。

如果open_mode是"r",主呼叫程式就可以使用被呼叫程式的輸出,通過函式返回的file指標,就可以能過stdio函式(如fread)來讀取程式的輸出;如果open_mode是"w",主呼叫程式就可以向被呼叫程式傳送資料,即通過stdio函式(如fwrite)向被呼叫程式寫資料,而被呼叫程式就可以在自己的標準輸入中讀取這些資料。

pclose函式用於關閉由popen建立出的關聯檔案流。pclose只在popen啟動的程序結束後才返回,如果呼叫pclose時被呼叫程序仍在執行,pclose呼叫將等待該程序結束。它返回關閉的檔案流所在程序的退出碼。

2、例子

很多時候,我們根本就不知道輸出資料的長度,為了避免定義乙個非常大的陣列作為緩衝區,我們可以以塊的方式來傳送資料,一次讀取乙個塊的資料並傳送乙個塊的資料,直到把所有的資料都傳送完。下面的例子就是採用這種方式的資料讀取和傳送方式。原始檔名為popen.c,**如下:

#include #include #include #include int main()

//關閉檔案流

pclose(read_fp);

pclose(write_fp);

exit(exit_success);

} exit(exit_failure);

}

執行結果如下:

從執行結果來看,達到了資訊篩選的目的。程式在程序ls中讀取資料,再把資料傳送到程序grep中進行篩選處理,相當於在shell中直接輸入命令:ls -l | grep rwxrwxr-x。

3、popen的實現方式及優缺點

當請求popen呼叫執行乙個程式時,它首先啟動shell,即系統中的sh命令,然後將command字串作為乙個引數傳遞給它。

這樣就帶來了乙個優點和乙個缺點。優點是:在linux中所有的引數擴充套件都是由shell來完成的。所以在啟動程式(command中的命令程式)之前先啟動shell來分析命令字串,也就可以使各種shell擴充套件(如萬用字元)在程式啟動之前就全部完成,這樣我們就可以通過popen啟動非常複雜的shell命令。

而它的缺點就是:對於每個popen呼叫,不僅要啟動乙個被請求的程式,還要啟動乙個shell,即每乙個popen呼叫將啟動兩個程序,從效率和資源的角度看,popen函式的呼叫比正常方式要慢一些。

三、pipe呼叫

如果說popen是乙個高階的函式,pipe則是乙個底層的呼叫。與popen函式不同的是,它在兩個程序之間傳遞資料不需要啟動乙個shell來解釋請求命令,同時它還提供對讀寫資料的更多的控制。

pipe函式的

原型如下:

#include int pipe(int file_descriptor[2]);
我們可以看到pipe函式的定義非常特別,該函式在陣列中牆上兩個新的檔案描述符後返回0,如果返回返回-1,並設定errno來說明失敗原因。

陣列中的兩個檔案描述符以一種特殊的方式連線起來,資料基於先進先出的原則,寫到file_descriptor[1]的所有資料都可以從file_descriptor[0]讀回來。由於資料基於先進先出的原則,所以讀取的資料和寫入的資料是一致的。

1、從函式的原型我們可以看到,它跟popen函式的乙個重大區別是,popen函式是基於檔案流(file)工作的,而pipe是基於檔案描述符工作的,所以在使用pipe後,資料必須要用底層的read和write呼叫來讀取和傳送。

2、不要用file_descriptor[0]寫資料,也不要用file_descriptor[1]讀資料,其行為未定義的,但在有些系統上可能會返回-1表示呼叫失敗。資料只能從file_descriptor[0]中讀取,資料也只能寫入到file_descriptor[1],不能倒過來。

例子:

首先,我們在原先的程序中建立乙個管道,然後再呼叫fork建立乙個新的程序,最後通過管道在兩個程序之間傳遞資料。原始檔名為pipe.c,**如下:

#include #include #include #include int main()

if(pid == 0)

else

}exit(exit_failure);

}

執行結果為:

可見,子程序讀取了父程序寫到filedes[1]中的資料,如果在父程序中沒有sleep語句,父程序可能在子程序結束前結束,這樣你可能將看到兩個輸入之間有乙個命令提示符分隔。

四、把管道用作標準輸入和標準輸出

下面來介紹一種用管道來連線兩個程序的更簡潔方法,我們可以把檔案描述符設定為乙個已知值,一般是標準輸入0或標準輸出1。這樣做最大的好處是可以呼叫標準程式,即那些不需要以檔案描述符為引數的程式。

為了完成這個工作,我們還需要兩個函式的輔助,它們分別是dup函式或dup2函式,它們的原型如下

#include int dup(int file_descriptor);

int dup2(int file_descriptor_one, int file_descriptor_two);

dup呼叫建立乙個新的檔案描述符與作為它的引數的那個已有檔案描述符指向同乙個檔案或管道。對於dup函式而言,新的檔案描述總是取最小的可用值。而dup2所建立的新檔案描述符或者與int file_descriptor_two相同,或者是第乙個大於該引數的可用值。所以當我們首先關閉檔案描述符0後呼叫dup,那麼新的檔案描述符將是數字0.例子

在下面的例子中,首先開啟管道,然後fork乙個子程序,然後在子程序中,使標準輸入指向讀管道,然後關閉子程序中的讀管道和寫管道,只留下標準輸入,最後呼叫execlp函式來啟動乙個新的程序od,但是od並不知道它的資料**是管道還是終端。父程序則相對簡單,它首先關閉讀管道,然後在寫管道中寫入資料,再關閉寫管道就完成了它的任務。原始檔為pipe2.c,**如下:

#include #include #include #include int main()

if(pid == 0)

else

}exit(exit_success);

}

執行結果為:

從執行結果中可以看出od程序正確地完成了它的任務,與在shell中直接輸入od -c和123的效果一樣。

五、關於管道關閉後的讀操作的討論

現在有這樣乙個問題,假如父程序向管道file_pipe[1]寫資料,而子程序在管道file_pipe[0]中讀取資料,當父程序沒有向file_pipe[1]寫資料時,子程序則沒有資料可讀,則子程序會發生什麼呢?再者父程序把file_pipe[1]關閉了,子程序又會有什麼反應呢?

當寫資料的管道沒有關閉,而又沒有資料可讀時,read呼叫通常會阻塞,但是當寫資料的管道關閉時,read呼叫將會返回0而不是阻塞。注意,這與讀取乙個無效的檔案描述符不同,read乙個無效的檔案描述符返回-1。

六、匿名管道的缺陷

linux程序間通訊——使用命名管道

中介紹。

Ubuntu下Linux程序間通訊 匿名管道

linux程序間通訊 匿名管道 linux程序間通訊 fifo 有名管道 linux程序間通訊 訊息佇列 linux程序間通訊 訊號量 linux程序間通訊 共享記憶體 linux提供了多種程序間通訊的方法,常見有管道 匿名 fifo 有名管道 訊息佇列 訊號量 共享記憶體,socket通訊。1.匿...

Linux程序間通訊 使用共享記憶體

一 什麼是共享記憶體 顧名思義。共享記憶體就是同意兩個不相關的程序訪問同乙個邏輯記憶體。共享記憶體是在兩個正在執行的程序之間共享和傳遞資料的一種很有效的方式。不同程序之間共享的記憶體通常安排為同一段物理記憶體。程序能夠將同一段共享記憶體連線到它們自己的位址空間中,全部程序都能夠訪問共享記憶體中的位址...

unix程序間通訊 使用管道通訊

使用管道需要注意以下4種特殊情況 假設都是阻塞i o操作,沒有設定o nonblock標誌 如果所有指向管道寫端的檔案描述符都關閉了 管道寫端的引用計數等於0 而仍然有程序從管道的讀端讀資料,那麼管道中剩餘的資料都被讀取後,再次read會返回0,就像讀到檔案末尾一樣。如果有指向管道寫端的檔案描述符沒...