整理自網絡html
Unix IPC包括:管道(pipe)、命名管道(FIFO)與信號(Signal)node
管道(pipe)網絡
管道可用於具備親緣關係進程間的通訊,有名管道克服了管道沒有名字的限制,所以,除具備管道所具備的功能外,它還容許無親緣關係進程間的通訊;數據結構
實現機制:函數
管道是由內核管理的一個緩衝區,至關於咱們放入內存中的一個紙條。管道的一端鏈接一個進程的輸出。這個進程會向管道中放入信息。管道的另外一端鏈接一個進程的輸入,這個進程取出被放入管道的信息。一個緩衝區不須要很大,它被設計成爲環形的數據結構,以便管道能夠被循環利用。當管道中沒有信息的話,從管道中讀取的進程會等待,直到另外一端的進程放入信息。當管道被放滿信息的時候,嘗試放入信息的進程會等待,直到另外一端的進程取出信息。當兩個進程都終結的時候,管道也自動消失。post
從原理上,管道利用fork機制創建,從而讓兩個進程能夠鏈接到同一個PIPE上。最開始的時候,上面的兩個箭頭都鏈接在同一個進程Process 1上(鏈接在Process 1上的兩個箭頭)。當fork複製進程的時候,會將這兩個鏈接也複製到新的進程(Process 2)。隨後,每一個進程關閉本身不須要的一個鏈接 (兩個黑色的箭頭被關閉; Process 1關閉從PIPE來的輸入鏈接,Process 2關閉輸出到PIPE的鏈接),這樣,剩下的紅色鏈接就構成了如上圖的PIPE。url
實現細節:spa
在 Linux 中,管道的實現並無使用專門的數據結構,而是藉助了文件系統的file結構和VFS的索引節點inode。經過將兩個 file 結構指向同一個臨時的 VFS 索引節點,而這個 VFS 索引節點又指向一個物理頁面而實現的。以下圖設計
有兩個 file 數據結構,但它們定義文件操做例程地址是不一樣的,其中一個是向管道中寫入數據的例程地址,而另外一個是從管道中讀出數據的例程地址。這樣,用戶程序的系統調用仍然是一般的文件操做,而內核卻利用這種抽象機制實現了管道這一特殊操做。code
關於管道的讀寫
管道實現的源代碼在fs/pipe.c中,在pipe.c中有不少函數,其中有兩個函數比較重要,即管道讀函數pipe_read()和管道寫函數pipe_wrtie()。管道寫函數經過將字節複製到 VFS 索引節點指向的物理內存而寫入數據,而管道讀函數則經過複製物理內存中的字節而讀出數據。固然,內核必須利用必定的機制同步對管道的訪問,爲此,內核使用了鎖、等待隊列和信號。
當寫進程向管道中寫入時,它利用標準的庫函數write(),系統根據庫函數傳遞的文件描述符,可找到該文件的 file 結構。file 結構中指定了用來進行寫操做的函數(即寫入函數)地址,因而,內核調用該函數完成寫操做。寫入函數在向內存中寫入數據以前,必須首先檢查 VFS 索引節點中的信息,同時知足以下條件時,才能進行實際的內存複製工做:
·內存中有足夠的空間可容納全部要寫入的數據;
·內存沒有被讀程序鎖定。
若是同時知足上述條件,寫入函數首先鎖定內存,而後從寫進程的地址空間中複製數據到內存。不然,寫入進程就休眠在 VFS 索引節點的等待隊列中,接下來,內核將調用調度程序,而調度程序會選擇其餘進程運行。寫入進程實際處於可中斷的等待狀態,當內存中有足夠的空間能夠容納寫入數據,或內存被解鎖時,讀取進程會喚醒寫入進程,這時,寫入進程將接收到信號。當數據寫入內存以後,內存被解鎖,而全部休眠在索引節點的讀取進程會被喚醒。
管道的讀取過程和寫入過程相似。可是,進程能夠在沒有數據或內存被鎖定時當即返回錯誤信息,而不是阻塞該進程,這依賴於文件或管道的打開模式。反之,進程能夠休眠在索引節點的等待隊列中等待寫入進程寫入數據。當全部的進程完成了管道操做以後,管道的索引節點被丟棄,而共享數據頁也被釋放。
Linux函數原型
#include <unistd.h> int pipe(int filedes[2]);
filedes[0]用於讀出數據,讀取時必須關閉寫入端,即close(filedes[1]);
filedes[1]用於寫入數據,寫入時必須關閉讀取端,即close(filedes[0])。
程序實例:
int main(void) { int n; int fd[2]; pid_t pid; char line[MAXLINE]; if(pipe(fd) 0){ /* 先創建管道獲得一對文件描述符 */ exit(0); } if((pid = fork()) 0) /* 父進程把文件描述符複製給子進程 */ exit(1); else if(pid > 0){ /* 父進程寫 */ close(fd[0]); /* 關閉讀描述符 */ write(fd[1], "\nhello world\n", 14); } else{ /* 子進程讀 */ close(fd[1]); /* 關閉寫端 */ n = read(fd[0], line, MAXLINE); write(STDOUT_FILENO, line, n); } exit(0); }
命名管道(named PIPE)
因爲基於fork機制,因此管道只能用於父進程和子進程之間,或者擁有相同祖先的兩個子進程之間 (有親緣關係的進程之間)。爲了解決這一問題,Linux提供了FIFO方式鏈接進程。FIFO又叫作命名管道(named PIPE)。
FIFO (First in, First out)爲一種特殊的文件類型,它在文件系統中有對應的路徑。當一個進程以讀(r)的方式打開該文件,而另外一個進程以寫(w)的方式打開該文件,那麼內核就會在這兩個進程之間創建管道,因此FIFO實際上也由內核管理,不與硬盤打交道。之因此叫FIFO,是由於管道本質上是一個先進先出的隊列數據結構,最先放入的數據被最早讀出來,從而保證信息交流的順序。FIFO只是借用了文件系統(file system,命名管道是一種特殊類型的文件,由於Linux中全部事物都是文件,它在文件系統中以文件名的形式存在。)來爲管道命名。寫模式的進程向FIFO文件中寫入,而讀模式的進程從FIFO文件中讀出。當刪除FIFO文件時,管道鏈接也隨之消失。FIFO的好處在於咱們能夠經過文件的路徑來識別管道,從而讓沒有親緣關係的進程之間創建鏈接
函數原型:
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *filename, mode_t mode); int mknode(const char *filename, mode_t mode | S_IFIFO, (dev_t) 0 );
其中pathname是被建立的文件名稱,mode表示將在該文件上設置的權限位和將被建立的文件類型(在此狀況下爲S_IFIFO),dev是當建立設備特殊文件時使用的一個值。所以,對於先進先出文件它的值爲0。