在某些特性上又不同於檔案,例如,當數據讀出後,則管道中就沒有數據了,但檔案沒有這個特性。
匿名半雙工管道在系統中是沒有實名的,並不可以在檔案系統中以任何方式看到該管道。它只是進程的一種資源,會隨著進程的結束而被系統清除。管道通信是在UNIX系統中套用比較頻繁的一種方式,例如使用grep查找。
上述命令中使用的是半雙工管道,即grep命令的輸入是ls命令的輸出。管道從數據流動方向上又分全雙工管道以及半雙工管道,當然全雙工管道現在某些系統還不支持,其在具體的實現過程中也只是在檔案打開的方式上有一點區別(在操作規則上也有一些不同,全雙工管道要相比半雙工複雜的多)。
匿名管道沒有名字,對於管道中使用的檔案描述符沒有路徑名,也就是不存在任何意義上的檔案,它們只是在記憶體中跟某一個索引節點相關聯的兩個檔案描述符。匿名半雙工管道的主要特性如下:
● 數據只能在一個方向上移動。
● 只能在具有公共祖先的進程間通信,即或是父子關係進程間、或是在兄弟關係進程間通信。
儘管有如此限制,半雙工管道還是最常用的通信方式。Linux環境下使用pipe函式創建一個匿名半雙工管道,其函式原型如下:
參數int fd[2]為一個長度為2的檔案描述符數組,fd[0]是讀出端,fd[1]是寫入端,函式的返回值為0表示成功,–1表示失敗。當函式成功返回,則自動維護了一個從fd[1]到fd[0]的數據通道。
下面實例演示了如何使用pipe函式創建管道以及關閉管道。程式中先使用函式pipe建立管道,並使用管道傳輸數據,在程式的結束部分,釋放掉管道占用的檔案資源(兩個檔案描述符),具體實現如下。
當對管道進行讀寫操作時,使用read和write函式對管道進行操作。當對一個讀端已經關閉的管道進行寫操作時,會產生信號SIGPIPE,說明管道讀端已經關閉,並且write操作返回為–1,errno的值為EPIPE,對於SIGPIPE信號可以進行捕捉處理。如果寫入進程不能捕捉或者乾脆忽略SIGPIPE信號,則寫入進程會中斷。
%注意:在進行讀寫管道時,對一個管道進行讀操作後,read函式返回為0,有兩種意義,一種是管道中無數據並且寫入端已經關閉。另一種是管道中無數據,寫入端依然存活。這兩種情況要根據需要分別處理。
從程式實例14.1中可以發現,單獨一個進程操作管道是沒有任何意義的,管道的套用一般體現在父子進程或者兄弟進程的通信。
如果要建立一個父進程到子進程的數據通道,可以先調用pipe函式緊接著調用 fork函式,由於子進程自動繼承父進程的數據段,則父子進程同時擁有管道的操作權,此時管道的方向取決於用戶怎么維護該管道,管道示意圖如圖14-3所示。
圖14-3 管道示意圖
當用戶想要一個父進程到子進程的數據通道時,可以在父進程中關閉管道的讀出端,相應的在子進程中關閉管道的輸出端,如圖14-3中圖B所示。相反的,當維護子進程到父進程的數據通道時,在父進程中關閉輸出,子進程中關閉讀入即可。總之,使用pipe 及fork組合,可以構造出所有的父進程與子進程,或子進程到兄弟進程的管道。
下面實例演示了使用pipe以及fork組合實現父子進程通信。程式中先使用pipe函式建立管道,使用fork函式創建子進程。在父子進程中維護管道的數據方向,並在父進程中向子進程傳送訊息,在子進程中接收訊息並輸出到標準輸出。
程式中使用pipe函式加fork組合,實現父進程到子進程的通信。程式在父進程段中關閉了管道的讀出端,並相應地在子進程中關閉了管道的輸入端,從而實現數據從父進程流向子進程。
管道在兄弟進程間套用時,應該先在父進程中建立管道,然後調用fork函式創建子進程,在父子進程中維護管道的數據方向。
%注意:這裡的問題是維護管道的順序,當父進程創建了管道,只有子進程已經繼承了管道後,父進程才可以執行關閉管道的操作。如果在fork之前已經關閉管道,子進程將不能繼承到可以使用的管道的。
下面實例演示了管道在兄弟進程間通信。下例中在父進程中創建管道,並使用fork函式創建2個子進程。在第1個子進程中傳送訊息到第2個子進程,第2個子進程中讀出訊息並處理。在父進程中,由於並不使用管道通信,所以什麼都不做,直接關閉了管道的兩端並退出。
從程式14-2和程式14-3中可以總結出管道操作的一個流程。父進程中先使用pipe函式創建管道,在調用fork函式創建子進程,在父子進程中維護管道的數據流向。程式退出時及時關閉管道的兩端,具體流程如圖14-4所示。
圖14-4 匿名管道的創建流程
管道操作的基本流程為:先創建一個管道,使用fork創建子進程, 在父子進程中關閉不需要的檔案描述符使用管道通信,程式結束。由於這是一個比較規範也是比較常用的管道使用模式,所以在ANSI/ISO C中將以上操作定義在兩個標準的庫函式popen和pclose中,它們的函式原型是:
函式popen 的參數 command 是一個在shell中可運行的命令字元串的指針,參數mode 是一個字元指針,這個參數只有兩種值可以使用,r或者w,分別表示popen函式的返回值是一個讀打開檔案指針,還是寫打開檔案指針。當函式失敗時返回值為NULL,並設定出錯變數errno。
popen函式先執行創建一個管道,然後調用fork函式創建子進程,緊接著執行一個exec函式調用, 調用/bin/sh –c來執行參數command中的命令字元串,然後函式返回一個標準的I/O檔案指針。返回的檔案指針類型與參數mode有關,如果參數mode是r則檔案指針連線到command 命令的標準輸出,如果是w則檔案指針連線到command命令的標準輸 入。為了關閉popen函式返回的檔案指針,可以調用pclose函式。pclose函式的參數stream是一個popen打開的檔案描述符,當函式失敗返回–1。
下面實例演示了使用popen和pclose函式實現調用shell命令cat來列印一個檔案到顯示器的程式。程式中先使用popen函式為cat命令創建一條數據管道,並指定數據管道從cat命令的輸出讀出數據。在後續的代碼中使用fgets函式讀出數據,並將數據顯示到標準輸出中。