UNIX高級編程總結-----進程間通信(管道)

        管道是UNIX 系統中比較古老的一種,他有兩個比較大的侷限性(缺點)。

        (1)歷史上的通道都是半雙工的(即數據只能往一個方向流)。某些系統是全雙工的,但是由於大多數系統都是半雙工的,爲了提高可移植性,應該將通道視作半雙工去使用。

        (2)管道的只能使用在有公共祖先的進程之間。一般情況下,一個進程由進程創建,在調用fork之後,這個管道就能在父子進程之間使用了。

        雖然有以上的兩大缺點,但是管道依然是UNIX中最常用的一個命令。在shell命令中的 “ | ”,就是一個管道。每當使用“ | ”時,shell 會爲其命一條命令創建一個進程,然後將管道前一條命令的標準輸出與後一條命令的標準輸入相連接。

        shell中管道的具體使用

1、管道的創建

        

        fd[0]爲讀而打開,fd[1] 爲寫而打開。fd[1]的輸出是fd[0]的輸入。

2、管道的通信

        (1)父進程創建管道,得到兩個⽂件描述符指向管道的兩端

        (2)父進程fork出子進程,⼦進程也有兩個⽂件描述符指向同⼀管道。

        (3)父進程關閉fd[0],子進程關閉fd[1],即⽗進程關閉管道讀端,⼦進程關閉管道寫端(因爲管道只支持單向通信)。⽗進程可以往管道⾥寫,⼦進程可以從管道⾥讀,管道是⽤環形隊列實現的,數據從寫端流⼊從讀端流出,這樣就實現了進程間通信。

3、書中代碼的實現,實現的父進程寫,子進程讀的一個管道實現。

#include "apue.h"

int
main(void)
{
    int        n;
    int        fd[2];
    pid_t    pid;
    char    line[MAXLINE];

    if (pipe(fd) < 0)
        err_sys("pipe error");
    if ((pid = fork()) < 0) {
        err_sys("fork error");
    } else if (pid > 0) {        /* parent */
        close(fd[0]);
        write(fd[1], "hello world\n", 12);
    } else {                    /* child */
        close(fd[1]);
        n = read(fd[0], line, MAXLINE);
        write(STDOUT_FILENO, line, n);
    }
    exit(0);
}

 4、管道讀數據會有四種情況

(1)讀端不讀,寫端一直寫

(2)寫端不寫,讀端一直讀

(3)讀端一直讀,寫端關閉

(4)寫端一直寫,讀端關閉

 5、書中介紹了一個實例,將一個文件的內容,分頁顯示出來,代碼如下:

#include "apue.h"
#include <sys/wait.h>

#define    DEF_PAGER    "/bin/more"        /* default pager program */

int
main(int argc, char *argv[])
{
    int        n;
    int        fd[2];
    pid_t    pid;
    char    *pager, *argv0;
    char    line[MAXLINE];
    FILE    *fp;

    if (argc != 2)
        err_quit("usage: a.out <pathname>");

    if ((fp = fopen(argv[1], "r")) == NULL)
        err_sys("can't open %s", argv[1]);
    if (pipe(fd) < 0)
        err_sys("pipe error");

    if ((pid = fork()) < 0) {
        err_sys("fork error");
    }
    else if (pid > 0)
    {                               /* parent */
        close(fd[0]);                /* close read end */

        /* parent copies argv[1] to pipe */
        while (fgets(line, MAXLINE, fp) != NULL)
        {
            n = strlen(line);
            if (write(fd[1], line, n) != n)
                err_sys("write error to pipe");
        }
        if (ferror(fp))
            err_sys("fgets error");

        close(fd[1]);    /* close write end of pipe for reader */

        if (waitpid(pid, NULL, 0) < 0)
            err_sys("waitpid error");
        exit(0);
    }
    else
    {                                       /* child */
        close(fd[1]);    /* close write end */
        if (fd[0] != STDIN_FILENO) {
            if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO)
                err_sys("dup2 error to stdin");
            close(fd[0]);    /* don't need this after dup2 */
        }

        /* get arguments for execl() */
        if ((pager = getenv("PAGER")) == NULL)
            pager = DEF_PAGER;
        if ((argv0 = strrchr(pager, '/')) != NULL)
            argv0++;        /* step past rightmost slash */
        else
            argv0 = pager;    /* no slash in pager */

        if (execl(pager, argv0, (char *)0) < 0)
            err_sys("execl error for %s", pager);
    }
    exit(0);
}

部分參考:Linux進程間通信-管道深入理解  (個人感覺在進程間通信方面,總結的不錯,值得參考)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章