linux 進程間通訊之管道

1.概述

1.1 一個管道是一個字節流:

  • 使用管道時是不存在消息或消息邊界,即讀取進程能夠讀取任意大小的數據塊,而無論寫入進程寫入管道的數據塊大小是什麼。
  • 管道傳遞的數據是順序的,即從管道中讀取出來的字節的順序與被寫入管道的順序一致。

1.2 從管道中讀取數據:

  • 讀取數據爲空的管道將會堵塞直到至少有一個字節被寫入到管道爲止。
  • 管道的寫入端沒有關閉,那麼讀取數據將會堵塞直到全部管道的寫入端關閉。
  • 管道的寫入端關閉,那麼進程能夠讀取任意大小的數據塊,直至讀完了管道中的全部數據即read()返回0 。

1.3 管道是單向的:

  • 管道中數據的傳遞方向是單向的,管道的一端用於寫入,另外一端用於讀取。

1.4 管道的容量是有限的:

  • 管道實際上是一個在內核內存中維護的緩衝器,這個緩衝器的存儲能力是有限的。
  • 管道被填滿以後,後續向管道寫入操做都會被堵塞直到有讀取進程讀取管道中的數據。

1.5 能夠確保寫入不超過PIPE_BUF字節的操做是原子的:

  • 若是多個進程寫入同一個管道,那麼若是他們在一個時刻寫入的數據量不超過PIPE_BUF字節,那麼就能夠確保寫入的數據不會混淆。
  • 寫入的數據塊大小超過了PIPE_BUF字節,那麼內核會將數據分割成幾個較小的片斷傳輸,在讀者從管道中消耗數據時再附加上後續的數據。(write()調用會阻塞直到全部數據被寫入到管道爲止),因此當多個寫入進程寫入大數據塊的時候,可能會出現數據交叉的現象。

2.建立和使用管道

#include<unistd.h>
int pipe(int filedes[2]);//return 0 on success,or -1 on error
複製代碼
  • filedes[0]表示管道的讀取端;shell

  • filedes[1]表示管道的寫入端;bash

  • 使用read()和write()系統調用來在管道上執行I/O。函數

建立完管道以後處理文件描述符

3.關閉未使用管道文件描述符

3.1 讀取進程需關閉其持有的管道寫入描述符的緣由:

read()會堵塞直到全部管道的寫入描述符關閉爲止。大數據

3.2 寫入進程需關閉其持有的管道讀取描述符的緣由:

當一個進程試圖向管道中寫入數據但沒有任何進程擁有該管道的打開着的讀取描述符的時候,內核會向寫入進程發送一個SIGPIPE信號(默認會殺死進程),進程能夠捕獲或忽略該信號,這樣write操做將會返回EPIPE的錯誤。收到SIGPIPE信號或獲得EPIPE錯誤能夠獲知管道的狀態,能夠避免寫入被堵塞的風險,由於一直寫入數據沒有讀取,將會充滿管道,那麼後續的寫入請求都將會被堵塞。ui

3.3 關閉未使用管道文件描述符的緣由:

當全部進程中的管道文件描述符都關閉以後纔會銷燬該管道以及釋放該管道佔用的資源以供其餘進程複用。同時,管道中的數據都會丟失。spa

4.管道能夠做爲一種進程同步的方法

父進程在建立子進程以前構建了一個管道。每一個子進程會繼承管道的寫入端的文件描述符並完成動做以後關閉這些描述符。當全部子進程都關閉了管道的寫入端的文件描述符以後,父進程在管道上的read()就會結束並返回文件結束(0)。這時,父進程就可以作其餘工做了。(父進程要關閉管道的寫入端,不然將永遠阻塞。)code

switch (fork()) {
        case -1:
                ERR_EXIT("fork");
        case 0:
                if (close(pfd[0]) == -1)
                    ERR_EXIT("close");
                
                //Child does some work,and lets parent know It‘s done
                
                if(close(pfd[1])==-1)
                    ERR_EXIT("close");

                //child now carries on to do other things...
               
                _exit(EXIT_SUCCESS);
        default:
                break;
    }

    if(close(pfd[1])==-1)
        ERR_EXIT("close");
    //parent may do other work,then synchronizes with children

    if(read(pfd[0],&dummy,1)!=0)//block
        ERR_EXIT("read");
 
複製代碼

5.經過管道與shell命令進行通訊:popen()

#include<stdio.h>
FIFE *popen(const char *command,const char *mode);
    //return file stream,or NULL on error
int pclose(FILE *stream);
    //return termination status of child process,or -1 on error
複製代碼

popen() 函數建立了一個管道,而後建立了一個子進程來執行shell,而shell又建立了一個子進程來執行command字符串。mode參數是一個字符串,它確調用進程是從管道中讀取數據(mode是r)仍是將數據寫入到管道中(mode是w)。cdn

mode的取值肯定了所執行的命令的標準輸出是鏈接到管道的寫入端仍是將其標準輸入鏈接到管道的讀取端。blog

調用進程fp--(fork(),exec())-->/bin/sh--(fork(),exec())-->命令(stdout)--(管道)-->調用進程fp繼承

a)mode is r

調用進程fp--(fork(),exec())-->/bin/sh--(fork(),exec())-->命令(stdin)

調用進程fp--(管道)-->命令stdin

b) mode is w

相關文章
相關標籤/搜索