歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

Linux進程間通信(二) 管道通信之無名管道及其基礎實驗

管道簡介

管道是Linux中進程間通信的一種方式,它把一個程序的輸出直接連接到另一個程序的輸 入(其實我更願意將管道比喻為農村澆地的管子)。Linux的管道主要包括兩種:無名管道和有名管道。這一節 主要講無名管道,首先介紹一下這兩個管道。(特點很重要啊!)

1、無名管道

無名管道是 Linux中管道通信的一種原始方法,如圖一(左)所示,它具有以下特點:

①  它只能用於具有親 緣關系的進程之間的通信(也就是父子進程或者兄弟進程之間);

②  它是一個半雙工的通信模 式,具有固定的讀端和寫端;

③   管道也可以看成是一種特殊的文件,對於它的讀寫也可以使 用普通的 read()、write()等函數。但它不是普通的文件,並不屬於其他任何文件系統並且只存在於內存中。

2、有名管道(FIFO)

有名管道是對無名管道的一種改進,如圖1(右)所示,它具有以下特點:

①  它可以使互不相關的兩個進程間實現彼此通信;

②  該管道可以通過路徑名來 指出,並且在文件系統中是可見的。在建立了管道之後,兩個進程就可以把它當做普通文件一樣進行讀寫操作 ,使用非常方便;

③  FIFO嚴格地遵循先進先出規則,對管道及FIFO的讀總是從開始處返回數據 ,對它們的寫則是把數據添加到末尾,它們不支持如 lseek()等文件定位操作。

無名管道及其系 統調用

1、管道創建與管道說明

管道是基於文件描述符的通信方式,當一個管道建立時,它會 創建兩個文件描述符fd[0]和fd[1],其中fd[0]固定用於讀管道,而fd[1]固定用於寫管道,如圖2所示,這樣 就構成了一個半雙工的通道。

管道關閉時只需要將這兩個文 件描述符關閉即可,可使用普通的close()函數逐個關閉各個文件描述符。

2、管道創建函數

創 建管道可以調用 pipe() 來實現,如下表

3、管道讀寫說明

用 pipe() 創建的管道兩端處於同一個進程中,由於管道主要是用於在不同的進 程間通信的,因此,在實際應用中沒有太大意義。實際上,通常先是創建一個管道,再調用fork()函數創建一 個子進程,該子進程會繼承父進程所創建的管道,這時,父子進程管道的文件描述符對應關系如下圖

此時的關系看似非常復雜,實際上卻已經給不同進程之間的讀寫創造了很好的條件。父子進程分別 擁有自己的讀寫通道,為了實現父子進程之間的讀寫,只需把無關的讀端或寫端的文件描述符關閉即可。例如 ,圖4中,將父進程的寫端fd[1]和子進程的讀端fd[0]關閉,則父子進程之間就建立起一條“子進程寫入父進 程讀取”的通道。   同樣,也可以將父進程的讀端fd[0]和子進程的寫端fd[1]關閉,則父子進程之間就 建立起一條“父進程寫入子進程讀取”的通道

另外,父進程還可以創建多個 子進程,各個子進程都繼承了相應的fd[0]和fd[1],此時,只需要關閉相應的端口就可以建立各子進程之間的 的通道。

4、管道讀寫注意點

●  只有在管道的讀端存在時,向管道寫入數據才有意義。 否則,向管道寫入數據的進程將收到內核傳來的 SIGPIPE 信號(通常為 Broken pipe錯誤)。

●  向管道寫入數據時,Linux將不保證寫入的原子性,管道緩沖區一有空閒區域,寫進程就會試圖向管道 寫入數據。如果讀進程不讀取管道緩沖區中的數據,那麼寫進程將會一直阻塞。

●  父子進程在 運行時,它們的先後次序並不能保證。因此,為了保證父子進程已經關閉了相應的文件描述符,可在兩個進程 中調用 sleep()函數。當然,這種調用不是很好的解決方法,以後我會用進程之間的同步與互斥機制來修改它 的!

基礎實驗

本實驗中,首先創建管道,之後父進程使用 fork()函數創建子進程,最後通過 關閉父進程的讀描述符fd[0]和子進程的寫描述符fd[1]來建立一個"父進程寫入子進程讀取"的管道 ,從而建立起它們之間的通信。

本實驗代碼如下,我上傳到網站,pipe.c點此下載

使用命令: gcc pipe.c -o pipe編譯後,運行:./pipe 可以看到如下結果

標准流管道

標准流管道函數說明

與Linux的文件操作中有基於文件流的標准I/O操作一樣, 管道的操作也支持基於文件流的的模式。這種基於文件流的管道主要是用來創建一個連接到另一個進程的管道 ,這裡的"另一個進程"也就是一個可以進行一定操作的可執行文件,例如,用戶執行“ls -l”或 者自己編寫的程序“./pipe”  等。由於這類操作很常用,因此標准流管道就將一系列的創建過程合並 到一個函數 popen()中完成,它所完成的工作有以下幾步:

①  創建一個管道

②  fork()創建一個子進程

③  在父子進程中關閉不需要的文件描述符

④  執行 exec 函數族調用

⑤  執行函數中所指定的命令

這個函數的使用可以大大減少代碼的編寫 量,但同時也有一些不利之處。例如,它不如前面管道創建的函數那樣靈活多變,並且用popen()創建的管道 必須使用標准I/O函數進行操作,而不能使用前面的 read()、write()一類不帶緩沖的I/O函數。與之相對應, 關閉用popen()創建的流管道必須使用函數 pclose(),該函數關閉標准I/O流,並等待命令執行結束。

函數格式

popen()函數和pclose()函數如下表:

基礎實驗2

本實驗中,使用popen()函數來執行“ls -l”命令。可以看出,popen()函數的使用能使 程序變得短小精悍。

standard_pipe.c文件下載: http://download.csdn.net/detail/mybelief321/5561891

執行結果如下

下一節講有名管道

Copyright © Linux教程網 All Rights Reserved