Linux 中的管道實現

2021-08-01 17:24:12 字數 3650 閱讀 2816

管道是一種最基本的ipc機制,作用於有血緣關係的程序之間,完成資料傳遞。呼叫pipe系統函式即可建立乙個管道。有如下特質:

1. 其本質是乙個偽檔案(實為核心緩衝區)

2. 由兩個檔案描述符引用,乙個表示讀端,乙個表示寫端。

3. 規定資料從管道的寫端流入管道,從讀端流出。

管道的原理: 管道實為核心使用環形佇列機制,借助核心緩衝區(4k)實現。

管道的侷限性:

① 資料自己讀不能自己寫。

② 資料一旦被讀走,便不在管道中存在,不可反覆讀取。

③ 由於管道採用半雙工通訊方式。因此,資料只能在乙個方向上流動。

④ 只能在有公共祖先的程序間使用管道。

常見的通訊方式有,單工通訊、半雙工通訊、全雙工通訊。

建立管道

int pipe(int pipefd[2]); 成功:0;失敗:-1,設定errno

函式呼叫成功返回r/w兩個檔案描述符。無需open,但需手動close。規定:fd[0] → r; fd[1] → w,就像0對應標準輸入,1對應標準輸出一樣。向管道檔案讀寫資料其實是在讀寫核心緩衝區。

管道建立成功以後,建立該管道的程序(父程序)同時掌握著管道的讀端和寫端。如何實現父子程序間通訊呢?通常可以採用如下步驟:

1. 父程序呼叫pipe函式建立管道,得到兩個檔案描述符fd[0]、fd[1]指向管道的讀端和寫端。

2. 父程序呼叫fork建立子程序,那麼子程序也有兩個檔案描述符指向同一管道。

3. 父程序關閉管道讀端,子程序關閉管道寫端。父程序可以向管道中寫入資料,子程序將管道中的資料讀出。由於管道是利用環形佇列實現的,資料從寫端流入管道,從讀端流出,這樣就實現了程序間通訊。

練習:父子程序使用管道通訊,父寫入字串,子程序讀出並,列印到螢幕。 【pipe.c】

思考:為甚麼,程式中沒有使用sleep函式,但依然能保證子程序執行時一定會讀到資料呢?

[cpp]view plain

copy

print?

#include 

#include 

#include 

#include 

#include 

void

sys_err(

const

char

*str)  

intmain(

void

)   else

if(pid == 0)  else

return

0;  

}  

使用管道需要注意以下4種特殊情況(假設都是阻塞i/o操作,沒有設定o_nonblock標誌):

1. 如果所有指向管道寫端的檔案描述符都關閉了(管道寫端引用計數為0),而仍然有程序從管道的讀端讀資料,那麼管道中剩餘的資料都被讀取後,再次read會返回0,就像讀到檔案末尾一樣。

2. 如果有指向管道寫端的檔案描述符沒關閉(管道寫端引用計數大於0),而持有管道寫端的程序也沒有向管道中寫資料,這時有程序從管道讀端讀資料,那麼管道中剩餘的資料都被讀取後,再次read會阻塞,直到管道中有資料可讀了才讀取資料並返回。

3. 如果所有指向管道讀端的檔案描述符都關閉了(管道讀端引用計數為0),這時有程序向管道的寫端write,那麼該程序會收到訊號sigpipe,通常會導致程序異常終止。當然也可以對sigpipe訊號實施捕捉,不終止程序。具體方法訊號章節詳細介紹。

4. 如果有指向管道讀端的檔案描述符沒關閉(管道讀端引用計數大於0),而持有管道讀端的程序也沒有從管道中讀資料,這時有程序向管道寫端寫資料,那麼在管道被寫滿時再次write會阻塞,直到管道中有空位置了才寫入資料並返回。

總結:① 讀管道: 1. 管道中有資料,read返回實際讀到的位元組數。

2. 管道中無資料:

(1) 管道寫端被全部關閉,read返回0 (好像讀到檔案結尾)

(2) 寫端沒有全部被關閉,read阻塞等待(不久的將來可能有資料遞達,此時會讓出cpu)

② 寫管道: 1. 管道讀端全部被關閉, 程序異常終止(也可使用捕捉sigpipe訊號,使程序不終止)

2. 管道讀端沒有全部關閉:

(1) 管道已滿,write阻塞。

(2) 管道未滿,write將資料寫入,並返回實際寫入的位元組數。

練習:使用管道實現父子程序間通訊,完成:ls | wc –l。假定父程序實現ls,子程序實現wc。

ls命令正常會將結果集寫出到stdout,但現在會寫入管道的寫端;wc –l 正常應該從stdin讀取資料,但此時會從管道的讀端讀。      【pipe1.c】

[cpp]view plain

copy

print?

#include 

#include 

#include 

intmain(

void

)   else

return

0;  

}  /*

*  程式不時的會出現先列印$提示符,再出程式執行結果的現象。

*  這是因為:父程序執行ls命令,將輸出結果給通過管道傳遞給

*  子程序去執行wc命令,這時父程序若先於子程序列印wc執行結果

*  之前被shell使用wait函式成功**,shell就會先於子程序列印

*  wc執行結果之前列印$提示符。

*  解決方法:讓子程序執行ls,父程序執行wc命令。或者在兄弟程序間完成。*/

程式執行,發現程式執行結束,shell還在阻塞等待使用者輸入。這是因為,shell → fork → ./pipe1, 程式pipe1的子程序將stdin重定向給管道,父程序執行的ls會將結果集通過管道寫給子程序。若父程序在子程序列印wc的結果到螢幕之前被shell呼叫wait**,shell就會先輸出$提示符。

練習:使用管道實現兄弟程序間通訊。 兄:ls  弟: wc -l  父:等待**子程序。

要求,使用「迴圈建立n個子程序」模型建立兄弟程序,使用迴圈因子i標示。注意管道讀寫行為。 【pipe2.c】

[cpp]view plain

copy

print?

#include 

#include 

#include 

intmain(

void

)    

}  if

(i == 0)  else

if(i == 1)  else

return

0;  

}  

測試:是否允許,乙個pipe有乙個寫端,多個讀端呢?是否允許有乙個讀端多個寫端呢? 【pipe3.c】

[cpp]view plain

copy

print?

#include 

#include 

#include 

#include 

#include 

intmain(

void

)    

for(i = 0; i 

}  if

(i == 0)  else

if(i == 1)  else

return

0;  

}  

Linux管道的實現機制

在linux中,管道是一種使用非常頻繁的通訊機制。從本質上說,管道也是一種檔案,但它又和一般的檔案有所不同,管道可以克服 使用檔案進行通訊的兩個問題,具體表現為 限制管道的大小。實際上,管道是乙個固定大小的緩衝區。在linux中,該緩衝區的大小為1頁,即4k位元組,使得它的大小不象文 件那樣不加檢驗...

Linux管道的實現機制

linux管道的實現機制 在linux中,管道是一種使用非常頻繁的通訊機制。從本質上說,管道也是一種檔案,但它又和一般的檔案有所不同,管道可以克服使用檔案進行通訊的兩個問題,具體表現為 限制管道的大小。實際上,管道是乙個固定大小的緩衝區。在linux中,該緩衝區的大小為1頁,即4k位元組,使得它的大...

Linux管道實現機制

管道的結構 在 linux 中,管道的實現並沒有使用專門的資料結構,而是借助了檔案系統的file結構和vfs的索引節點inode。通過將兩個 file 結構指向同乙個臨時的 vfs 索引節點,而這個 vfs 索引節點又指向乙個物理頁面而實現的。如圖所示。管道的讀寫 管道實現的源 在fs pipe.c...