1. 程式人生 > >Linux程序間通訊之——管道(整理)

Linux程序間通訊之——管道(整理)

 程序間通訊 fork pipe pie_t 等用法(管道機制 通訊)

每個程序各自有不同的使用者地址空間,任 何一個程序的全域性變數在另一個程序中都看不到,所以程序之間要交換資料必須通過核心,在核心中開闢一塊緩衝 區,程序1把資料從使用者空間拷到核心緩衝區,程序2再從核心緩衝區把資料讀走,核心提供的這種機制稱為程序間通訊(IPC,InterProcess Communication)。如下圖所示。

圖 30.6. 程序間通訊

4.1. 管道

管道建立的原型函式為: int pipe(int fd[2]); 其中fd[2]為用於建立的管道的兩端,其中fd[0]為讀端,fd[1]為寫端,這兩端任務是固定的,不能混亂,如果混亂,將會導致錯誤出現

調 用pipe函式時在核心中開闢一塊緩衝區(稱為管道)用於通訊,它有一個讀端一個寫端,然後通過filedes引數傳出給使用者程式兩個檔案描述 符,filedes[0]指向管道的讀端,filedes[1]指向管道的寫端(很好記,就像0是標準輸入1是標準輸出一樣)。所以管道在使用者程式看起來 就像一個開啟的檔案,通過read(filedes[0]);或者write(filedes[1]);向這個檔案讀寫資料其實是在讀寫核心緩衝區。 pipe函式呼叫成功返回0,呼叫失敗返回-1。 

int pipe(int filedes[2]) 中的兩個檔案描述符被強制規定filedes[0]只能指向管道的讀端,如果進行寫操作就會出現錯誤;同理filedes[1]只能指向管道的寫端,如果進行讀操作就會出現錯誤
開闢了管道之後如何實現兩個程序間的通訊呢?比如可以按下面的步驟通訊。

圖 30.7. 管道

 

  1. 父程序呼叫pipe開闢管道,得到兩個檔案描述符指向管道的兩端。
2. 父程序呼叫fork建立子程序,那麼子程序也有兩個檔案描述符指向同一管道。
3. 父程序關閉管道讀端,子程序關閉管道寫端。父程序可以往管道里寫,子程序可以從管道里讀,管道是用環形佇列實現的,資料從寫端流入從讀端流出,這樣就實現了程序間通訊。

注:之前一直沒明白為什麼在這裡父程序要關閉管道讀端,並且子程序要關閉管道寫端,想了很久終於想通了...,原因如下:因為上面的這個程式是要模擬父程序和子程序的管道讀寫操作,其中父程序用於向管道中寫入資料,子程序用於向管道中讀取資料,因此開始要關閉父程序的讀檔案描述符filedes[0], 以及關閉子程序的寫檔案描述符filedes[1],這是為了模擬這個過程。

        然後至於為什麼父程序關閉管道的讀檔案描述符filedes[0]後子程序還能讀取管道的資料,是因為系統維護的是一個檔案的檔案描述符表的計數,父子程序都各自有指向相同檔案的檔案描述符,當關閉一個檔案描述符時,相應計數減一,當這個計數減到0時,檔案就被關閉,因此雖然父程序關閉了其檔案描述符filedes[0],但是這個檔案的檔案描述符計數還沒等於0,所以子程序還可以讀取。也可以這麼理解,父程序和子程序都有各自的檔案描述符,因此雖然父程序中關閉了filedes[0],但是對子程序中的filedes[0]沒有影響


檔案表中的每一項都會維護一個引用計數,標識該表項被多少個檔案描述符(fd)引用,在引用計數為0的時候,表項才會被刪除。所以呼叫close(fd)關閉子程序的檔案描述符,只會減少引用計數,但是不會使檔案表項被清除,所以父程序依舊可以訪問

        最後需要注意,在linux的pipe管道下,在寫端進行寫資料時,不需要關閉讀端的緩衝檔案(即不需要讀端的檔案描述符計數為0),但是在讀端進行讀資料時必須先關閉寫端的緩衝檔案(即寫端的檔案描述符計數為0)然後才能讀取資料。

使用管道有一些限制:
? 兩個程序通過一個管道只能實現單向通訊,比如上面的例子,父程序寫子程序讀,如果有時候也需要子程序寫父程序讀,就必須另開一個管道。請讀者思考,如果只開一個管道,但是父程序不關閉讀端,子程序也不關閉寫端,雙方都有讀端和寫端,為什麼不能實現雙向通訊?
? 管道的讀寫端通過開啟的檔案描述符來傳遞,因此要通訊的兩個程序必須從它們的公共祖先那裡繼承管道檔案描述符。上面的例子是父程序把檔案描述符傳給子程序 之後父子程序之間通訊,也可以父程序fork兩次,把檔案描述符傳給兩個子程序,然後兩個子程序之間通訊,總之需要通過fork傳遞檔案描述符使兩個程序 都能訪問同一管道,它們才能通訊。
使用管道需要注意以下4種特殊情況(假設都是阻塞I/O操作,沒有設定O_NONBLOCK標誌):
1. 如果所有指向管道寫端的檔案描述符都關閉了(管道寫端的引用計數等於0),而仍然有程序從管道的讀端讀資料,那麼管道中剩餘的資料都被讀取後,再次read會返回0,就像讀到檔案末尾一樣。
2. 如果有指向管道寫端的檔案描述符沒關閉(管道寫端的引用計數大於0),而持有管道寫端的程序也沒有向管道中寫資料,這時有程序從管道讀端讀資料,那麼管道中剩餘的資料都被讀取後,再次read會阻塞,直到管道中有資料可讀了才讀取資料並返回。
3. 如果所有指向管道讀端的檔案描述符都關閉了(管道讀端的引用計數等於0),這時有程序向管道的寫端write,那麼該程序會收到訊號SIGPIPE,通常會導致程序異常終止。在第 33 章 訊號會講到怎樣使SIGPIPE訊號不終止程序。
4. 如果有指向管道讀端的檔案描述符沒關閉(管道讀端的引用計數大於0),而持有管道讀端的程序也沒有從管道中讀資料,這時有程序向管道寫端寫資料,那麼在管道被寫滿時再次write會阻塞,直到管道中有空位置了才寫入資料並返回。
管道的這四種特殊情況具有普遍意義。在第 37 章 socket程式設計要講的TCP socket也具有管道的這些特性。

有名管道

下面我們看一下有名管道:上面的管道我們很容易看得出它的侷限性,那麼有名管道就會解決那種問題,即他可以使任意兩個程序之間通訊。有名管道是一個存在於硬碟上的檔案。 有兩個函式可以建立有名管道: int mknod(const char *path,mode_t mod,dev_t dev); int mkfifo(const char *path,mode_t mode); 有名管道和管道的使用方法相同,只是使用有名管道時必須使用open()函式將其開啟。 值得注意的是:呼叫open()開啟有名管道的程序可能會被阻塞,但如果同時用讀寫方式(O_RDWR)開啟,則一定不會阻塞,如果以只讀方式開啟,則一定會阻塞直到有寫的程序開啟管道,同樣以寫的方式開啟的時候也會被阻塞,知道有讀的程序開啟管道,下面我們通過一個具體的例子更深一步的瞭解有名管道的使用方法:

點選(此處)摺疊或開啟

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<string.h>
  4. #include<fcntl.h>
  5. #include<sys/types.h>
  6. #include<sys/stat.h>
  7. #define FIFO_NAME "myfifo"
  8. #define BUF_SIZE 1024
  9. int main()
  10. {
  11.     int fd;
  12.     char buf[BUF_SIZE]="hello procwrite,i come frome process named procread";
  13. //    umask(0);
  14.     if(mkfifo(FIFO_NAME,S_IFIFO|0666)==-1){
  15.         perror("mkfifo error!");
  16.         exit(0);
  17.     }
  18.     if((fd=open(FIFO_NAME,O_WRONLY))==-1){
  19.         perror("open fifo error!");
  20.         exit(0);
  21.     }
  22.     write(fd,buf,strlen(buf)+1);
  23.     close(fd);
  24.     exit(0);
  25. }

點選(此處)摺疊或開啟

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<unistd.h>
  4. #include<sys/stat.h>
  5. #include<fcntl.h>
  6. #include<sys/types.h>
  7. #define FIFO_NAME "myfifo"
  8. #define BUF_SIZE 1024
  9. int main(void)
  10. {
  11.     int fd;
  12.     char buf[BUF_SIZE];
  13. //    umask(0);
  14.     fd=open(FIFO_NAME,O_RDONLY);
  15.     read(fd,buf,BUF_SIZE);
  16.     printf("read content:%s\n",buf);
  17.     exit(0);
  18. }

剛開始由於好奇,當程序退出之後我就看見目錄下面生成了一個資料夾myfifo,奇怪的是與其他資料夾的顏色是不同的,我就試圖察看它的內容,結果裡面是空的,我當時就納了悶了,明明是一個資料夾,明明寫進去東西的,而且另一個程序也從中讀出了內容阿,難道是內容被讀走了以後就不存在在有名管道中了,但是它不也是一個檔案麼,寫進去的內容怎麼會不見呢,難道不是一般的檔案?於是上網查了查?結果如下: 有名管道是有名有形的,為了使用這種管道Linux中設立了一個專門的特殊檔案系統--管道檔案,它存在於檔案系統中,任何程序可以在任何時候通過有名管道的路徑和檔案來訪問管道,但是在磁碟上的只是一個節點,而檔案的資料則只存在於記憶體緩衝頁面中與普通管道一樣。哦,原來是這樣,看來它還確實跟普通的檔案不一樣呢!

相關推薦

Linux程序通訊——管道整理

 程序間通訊 fork pipe pie_t 等用法(管道機制 通訊) 每個程序各自有不同的使用者地址空間,任 何一個程序的全域性變數在另一個程序中都看不到,所以程序之間要交換資料必須通過核心,在核心中開闢一塊緩衝 區,程序1把資料從使用者空間拷到核心緩衝區,程序2再從

Linux系統程式設計——程序通訊管道pipe

管道的概述 管道也叫無名管道,它是是 UNIX 系統 IPC(程序間通訊) 的最古老形式,所有的 UNIX 系統都支援這種通訊機制。 無名管道有如下特點: 1、半雙工,資料在同一時刻只能在一個方向上流動。 2、資料只能從管道的一端寫入,從另一端讀出。

Linux -- 程序通訊管道

管道是Linux裡的一種檔案型別,同時也是Linux系統下程序間通訊的一種方式 建立一個管道檔案有兩種方式: 1.  Shell 下命令 mkfifo + filename,即建立一個有名管道 2.

Linux 程序通訊管道

程序之間的通訊之管道 目錄 1 無名管道    管道是一種最基本的IPC機制,作用於父子程序之間,完成資料傳遞。 管道有以下特性: 1.其本質是一個偽檔案(實為核心緩衝區)其本質是一個偽檔案(實為核心緩衝區) 2.由兩

程序通訊管道pipe

管道的概述 管道也叫無名管道,它是是 UNIX 系統 IPC(程序間通訊) 的最古老形式,所有的 UNIX 系統都支援這種通訊機制。 無名管道有如下特點: 1、半雙工,資料在同一時刻只能在一個方向上流動。 2、資料只能從管道的一端寫入,從另一端讀出。 3、寫入

Linux程序通訊管道通訊詳解

        在學習程序的時候,我們瞭解到了程序的獨立性:程序之間是相互獨立的,每個程序有自己的虛擬地址空間,並且虛擬地址空間通過頁表的對映,對映到屬於自己的實體記憶體上。並且各個程序之間互相不影響,執行自己的程式碼。    

Linux下的程序通訊管道

在Linux下,每個程序各自有不同的使用者地址空間,任何一個程序的全域性變數在另一個程序中都看不到所以進 程之間要交換資料必須通過核心,在核心中開闢一塊緩衝區,程序1把資料從使用者空間拷到核心緩衝區,程序2再從核心緩衝區把資料讀走,核心提供的這種機制稱為程序間通

linux 程序通訊FIFO

1.概述 FIFO與管道幾乎類似,所以FIFO也是一個位元組流,從FIFO讀取的順序也是與被寫入FIFO的順序一致,容量是也有限的,也是可以確保寫入不超過PIPE_BUF位元組的操作是原子的,FIFO的本質也是一個管道,但傳遞方向是可以雙向的,它們兩者之間的最大差別在於FIFO在檔案系統中擁有一個名稱,並且

程序通訊管道--pipe和fifo使用

匿名管道pipe 函式原型: #include <unistd.h> int pipe(int fildes[2]); 引數說明 fildes是我們傳入的陣列,也是一個傳出引數。fildes[0]是讀端,fildes[1]是寫端。 返回值 成功呼叫返回0。 失敗呼叫返回-1且

Linux程序通訊——01管道

程序間通訊: 對於每個程序來說,都有一份屬於它自己的記憶體資源,並且獨佔這份記憶體資源。而程序間通訊的目的就是讓不同的程序間能看到一份公共的資源。所有交換的資料必須通過核心來傳遞,在核心中開闢一塊緩衝區,通過這塊緩衝區來實現資料的傳遞,核心提供的這種機制稱為程序間通訊。

LinuxLinux程序通訊訊息佇列

1、訊息佇列概念引入    訊息佇列提供了一個從一個程序向另外一個程序傳送一塊資料的方法每個資料塊都被認為是有一個型別,接收者程序接收的資料塊可以有不同的型別值訊息佇列也有管道一樣的不足,就是每個訊息的最大長度是有上限的(MSG

Linux程序通訊訊號通訊

訊號通訊是Linux程序間通訊的一種方式。 1.什麼是訊號? 訊號是系統響應某些條件而產生的一個事件,接收到該訊號的程序會相應地採取一些措施。例如我們在windows系統中想強制結束一個程式我們需要用到的是工作管理員,而在Linux中,我們是通過訊號來實現的,執

程序通訊管道通訊(PIPE匿名管道)

#include<stdio.h> #include<errno.h> #include<fcntl.h> #include<unistd.h> #include<stdlib.h> #include<string.h> int

Linux程序通訊pipe

1、管道(PIPE)        從概念上說,管道是兩個程序之間的一個connection,因此一個程序的標準輸出就變成了另一個程序的標準輸入。在Unix作業系統中,管道用於程序間通訊(inter-process communication). (1

程序通訊管道PIPE

在前面程序通訊概念和程序通訊方式,我們瞭解了程序通訊的簡單概念以及4種程序通訊的方式,今天我們將要通過具體例項來學習,理解程序通訊方式中的管道(PIPE)。 本文所有程式碼都在Ubuntu16.04測試。 我們在前面已經瞭解了常用的程序間通訊方式,它們大

Linux 程序通訊方式 pipe函式

Linux 程序間通訊方式有以下幾種: 1-》管道(pipe)和有名管道(fifo). 2-》訊息佇列 3-》共享記憶體 4-》訊號量 5-》訊號(signal) 6-》套接字(sicket) 在這裡我們看一下第一種====管道(pipe)。有名管道(fifo)見其它文章。

Linux程序通訊POSIX訊息佇列

訊息佇列可認為是一個訊息連結串列,它允許程序之間以訊息的形式交換資料。有足夠寫許可權的程序或執行緒可往佇列中放置訊息,有足夠讀許可權的程序或執行緒可從佇列中取走訊息。每個訊息都是一個記錄,它由傳送者賦予一個優先順序。與管道不同,管道是位元組流模型,沒有訊息邊界。

linux 程序通訊訊號

Linux訊號(signal) 機制分析 轉載至:https://www.cnblogs.com/hoys/archive/2012/08/19/2646377.html 【摘要】本文分析了Linux核心對於訊號的實現機制和應

Linux程序通訊POSIX共享記憶體

共享記憶體是最高效的IPC機制,因為它不涉及程序之間的任何資料傳輸。這種高效率帶來的問題是,我們必須用其他輔助手段來同步程序對共享記憶體的訪問,否則會產生競態條件。因此,共享記憶體通常和其他程序間通訊方式一起使用。 Linux下有三種共享記憶體的IPC技術:S

Linux程序通訊訊號量(semaphore)、訊息佇列(Message Queue)和共享記憶體(Share Memory)

System V 程序通訊方式:訊號量(semaphore)、訊息佇列(Message Queue)和共享記憶體(Share Memory) 訊號量 訊號量(semaphore)實際是一個整數,它的值由多個程序進行測試(test)和設定(set)。就每個程序所關心的測試和