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进程间通信-管道深入理解  (个人感觉在进程间通信方面,总结的不错,值得参考)

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