管道:也是文件,可以用於有血緣關系的進程間的通信。 實現機制:
兩個或多個進程之間想要通信由於他們各擁有自己的地址空間所以必須有一塊公共的空間,而這塊空間就需要內核為他們提供,也就是緩沖區。
管道有兩端一端負責輸入,一端負責輸出,所以兩端就分別連接兩個進程。進程1負責將數據輸入到緩沖區,進程2將緩沖區的數據拿出來,這樣就實現了兩個進程的通信。
管道被設計成為環形的數據結構,以便管道可以被循環利用。當管道中沒有信息的話,從管道中讀取的進程會等待,直到另一端的進程放入信息。當管道被放滿信息的時候,嘗試放入信息的進程會等待,直到另一端的進程取出信息。當兩個進程都終結的時候,管道也自動消失。
int pipe(int fd[2]);
功能: 創建一個簡單的管道,若成功則為數組fd分配兩個文件描述符,其中fd[0] 用於讀取管道,fd[1]用於寫入管道。
返回:成功返回0,失敗返回-1;
[code]#include<stdio.h> #include<unistd.h> #include<errno.h> #include<string.h> #include<sys/wait.h> int main() { int _pipe[2];//標識管道的兩種狀態(0號為讀端,1號為寫端) int ret=pipe(_pipe); if(ret==-1) { printf("create pipe failed! error code is%d\n",errno); return -1; } pid_t id=fork(); if(id<0) { printf("fork error!"); return 2; } else if(id==0) { //child close(_pipe[0]);//關閉讀端 int i=0; char *mes=NULL; while(i<10) { mes="i am child\n"; write(_pipe[1],mes,strlen(mes)); sleep(1); i++; } close(_pipe[1]);//關閉寫端 } else { //farther close(_pipe[1]); char mes[100]; int j=0; while(j<10) { memset(mes,'\0',sizeof(mes)); int ret=read(_pipe[0],mes,sizeof(mes)); sleep(1); printf("%s:code is:%d\n",mes,ret); printf("count:%ld\n",count); j++; } if(waitpid(id,NULL,0)<0)//等待子進程 { return 3; } } return 0; }在 Linux 中,管道的實現並沒有使用專門的數據結構,而是借助了文件系統的file結構和VFS的索引節點inode。通過將兩個 file 結構指向同一個臨時的 VFS 索引節點,而這個 VFS 索引節點又指向一個物理頁面而實現的。
有兩個 file 數據結構,但它們定義文件操作例程地址是不同的,其中一個是向管道中寫入數據的例程地址,而另一個是從管道中讀出數據的例程地址。這樣,用戶程序的系統調用仍然是通常的文件操作,而內核卻利用這種抽象機制實現了管道這一特殊操作。
關於管道的讀寫
管道實現的源代碼在fs/pipe.c中,在pipe.c中有很多函數,其中有兩個函數比較重要,即管道讀函數pipe_read()和管道寫函數pipe_wrtie()。管道寫函數通過將字節復制到 VFS 索引節點指向的物理內存而寫入數據,而管道讀函數則通過復制物理內存中的字節而讀出數據。當然,內核必須利用一定的機制同步對管道的訪問,為此,內核使用了鎖、等待隊列和信號。
當寫進程向管道中寫入時,它利用標准的庫函數write(),系統根據庫函數傳遞的文件描述符,可找到該文件的 file 結構。file 結構中指定了用來進行寫操作的函數(即寫入函數)地址,於是,內核調用該函數完成寫操作。寫入函數在向內存中寫入數據之前,必須首先檢查 VFS 索引節點中的信息,同時滿足如下條件時,才能進行實際的內存復制工作:
·內存中有足夠的空間可容納所有要寫入的數據;
·內存沒有被讀程序鎖定。
如果同時滿足上述條件,寫入函數首先鎖定內存,然後從寫進程的地址空間中復制數據到內存。否則,寫入進程就休眠在 VFS 索引節點的等待隊列中,接下來,內核將調用調度程序,而調度程序會選擇其他進程運行。寫入進程實際處於可中斷的等待狀態,當內存中有足夠的空間可以容納寫入數據,或內存被解鎖時,讀取進程會喚醒寫入進程,這時,寫入進程將接收到信號。當數據寫入內存之後,內存被解鎖,而所有休眠在索引節點的讀取進程會被喚醒。
管道的讀取過程和寫入過程類似。但是,進程可以在沒有數據或內存被鎖定時立即返回錯誤信息,而不是阻塞該進程,這依賴於文件或管道的打開模式。反之,進程可以休眠在索引節點的等待隊列中等待寫入進程寫入數據。當所有的進程完成了管道操作之後,管道的索引節點被丟棄,而共享數據頁也被釋放。