匿名管道

LINUX進程通訊shell

公開的交流方式有:信號量,消息隊列,共享內存,有名管道,文件socket

祕密的信息僅限於交流雙方知道的有:信號通訊,無名管道通訊和socket通訊函數

一.匿名管道pipespa

   #include <unistd.h>設計

    int pipe(int filedes[2]);code

管道做用於有血緣關係的進程之間,經過fork來傳遞 blog

調用pipe函數時在內核中開闢一塊緩衝區(稱爲管道)用於通訊,它有一個讀端一個寫端,而後經過filedes參數傳出給用戶程序兩個文件描述符,filedes[0]指向管道的讀端,filedes[1]指向管道的寫端(很好記,就像0是標準輸入1是標準輸出同樣)。因此管道在用戶程序看起來就像一個打開的文件,經過read(filedes[0]);或者write(filedes[1]);向這個文件讀寫數據實際上是在讀寫內核緩衝區。pipe函數調用成功返回0,調用失敗返 回-1。繼承

     1.父進程調用pipe開闢管道,獲得兩個文件描述符指向管道的兩端。隊列

     2.父進程調用fork建立子進程,那麼子進程也有兩個文件描述符指向同一管道。進程

     3.父進程關閉管道讀端,子進程關閉管道寫端。父進程能夠往管道里寫,子進程能夠從管道里讀,管道是用環形隊列實現的,數據從寫端流入從讀端流出,這樣就實現了進程間通訊。

使用管道須要注意如下4種特殊狀況(假設都是阻塞I/O操做,沒有設置O_NONBLOCK標 志): 

1.寫端關閉,讀端將剩餘數據讀出後,再read會返回0

2.寫端未關閉,讀端將剩餘數據讀出後,再次read會阻塞,直到管道中有數據可讀了纔讀取數據並返回

3.讀端都關閉了,寫端write,那麼該進程會收到信號SIGPIPE,一般會致使進程異常終止。

4.讀端未關閉,但未read,那麼在管道被寫滿時再次write會阻塞,直到管道中有空位置了才寫入數據並返回。

管道的主要侷限性正體如今它的特色上:

1.    只支持單向數據流;

2.    只能用於具備親緣關係的進程之間;

3.    沒有名字;

4.    管道的緩衝區是有限的(管道制存在於內存中,在管道建立時,爲緩衝區分配一個頁面大小);

5.    管道所傳送的是無格式字節流,這就要求管道的讀出方和寫入方必須事先約定好數據的格式,好比多少字節算做一個消息(或命令、或記錄)等等

默認建立的爲阻塞管道,若是想設置爲非阻塞管道, fcntl函數設置O_NONBLOCK標誌 

實踐經驗:在進程通訊中,沒法判斷每次通訊的字節數,即沒法對數據進行自動拆分,致使子進程一次性讀取父進程兩次通訊的報文狀況,爲了能正常拆分發送報文,咱們常採用如下幾種方法:

1.固定長度,2.顯示長度;每一個報文有長度域和數據域組成。3.短鏈接,進程間每次須要通訊時,建立一個通訊線路,發送一條報文後,當即廢棄這條通訊線路.Soket通訊中經常使用。

經典模型:

一 。 單向管道流

二 。 雙向管道流模型

三。鏈接標準I/O的標準管道模型

設計一個父進程標準輸出流鏈接到子進程標準輸入流的管道

使用函數dup2重定向

(1) 建立管道,返回無名管道的兩個文件描述符fildes[0]和fildes[1]。
(2) 建立子進程,子進程中繼承無名管道文件描述符。
(3) 父進程關閉管道的輸出端,即關閉只讀文件描述符fileds[0]。
(4) 父進程將標準輸出(stdout,文件描述符1)重定向爲文件描述符fileds[1]。
(5) 子進程關閉管道的輸入端,即關閉只寫文件描述符fileds[1]。
(6) 子進程將標準輸入(stdin,文件描述符0)重定向爲文件描述符fileds[0]。

代碼示例:

#include <unistd.h>
#include <stdio.h>
int main()
{
    int fildes[2];
    pid_t pid;
    int i, j;
    char buf[256];
    if (pipe(fildes) < 0 || (pid = fork()) < 0) /* 建立管道和子進程 */
    {
        fprintf(stderr, "error!\n");
        return 1;
    }
    if (pid == 0)
    {
    /* ―――――――――――――――――子進程―――――――――――――――――― */
        close(fildes[1]);                  
        dup2(fildes[0], 0);                 /* 重定向stdin到fildes[0]中 */
        close(fildes[0]);
        gets(buf);                          /* 讀入輸入,實際上是讀取父進程輸出 */
        fprintf(stderr, "child:[%s]\n", buf);
        return 2;
    }
    /* ―――――――――――――――――父進程―――――――――――――――――― */
    close(fildes[0]);
    dup2(fildes[1], 1);                     /* 重定向stdout到fildes[1]中 */
    close(fildes[1]);
    puts("Hello!");                         /* 輸出,同時增長子進程的輸入信息*/
    return 0;                          
}

父進程的標準輸出已經重定向到管道中,故父進程puts未能將結果打印到屏幕上

四。popen模型

#include <stdio.h>

FILE *popen(const char* command, char* type);

int pclose (FILE* stream);

popen工做原理:相似於system,首先fork一個子進程,而後調用exec執行參數command中給定的shell命令。不一樣的是,函數popen自動在父進程和exec建立的子進程之間創建了一個管道。

參數type:r  建立與子進程的標準輸出鏈接的管道(管道數據由子進程流向父進程)

open調用成功返回標準I/O的FILE文件流,它的讀寫屬性由參數type決定,調用失敗返回NULL

pclose關閉popen打開的文件流,成功調用返回exec進程退出時的狀態,不然返回-1。

      w 建立與子進程的標準輸出鏈接的管道(管道數據由父進程流向子進程)

/*------------popen----------*/
#include <stdio.h>
void main()
{
    FILE *out, *in;
    if ( (out = popen( "grep init", "w" ) ) == NULL )
    {
        fprintf( stderr, "error!/n" );
        return;
    }
    if ( (in = popen( "ps -ef", "r" ) ) == NULL )
    {
        fprintf( stderr, "error !/n" );
        return;
    }
    while ( fgets( buf, sizeof(buf), in ) ) /*讀取ps -ef結果*/
        fputs( buf, out );
    pclose( out );
    pclose( in );
}

命令ps -ef: 分析當前運行的所有進程,並將結果打印到屏幕上。

進程「grep init」從輸入的字符串中查找包含字符「init」的子串,並打印結果。  

相關文章
相關標籤/搜索