Chinaunix首页 | 论坛 | 博客
  • 博客访问: 836764
  • 博文数量: 91
  • 博客积分: 2544
  • 博客等级: 少校
  • 技术积分: 1885
  • 用 户 组: 普通用户
  • 注册时间: 2006-12-12 09:08
文章存档

2016年(10)

2014年(2)

2013年(4)

2012年(23)

2011年(23)

2010年(13)

2009年(14)

2007年(2)

分类: LINUX

2009-06-11 10:35:30

进程通信
 
1, 管道(pipe)

 在理解管道时需要注意的地方:
 .管道是半双工的,不能假设它是全双工的。
 .它只能用在有公共祖先的进程之间。比如shell几个命令之间用管道连接,但父进程都是终端进程。
 
1.1 管道的创建
 #include
 int pipe(int filedes[2]);
DESCRIPTION
       pipe()  creates  a  pair  of file descriptors, pointing to a pipe inode, and places  them in the array pointed to by filedes.  filedes[0] is for reading, filedes[1]  is  for writing.
RETURN VALUE
       On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.
 可以这样理解:filedes[0]:可以看着是标准读描述符0;而fileds[1]:可以看做是标准写描述符1。
 
*在使用pipe时要注意:  
 .fstat可以得到该管道的文件描述符,用S_ISFIFO来测试管道。
 .单个进程创建管没有任何意义,一般是父子进程协同进行,关闭多余的描述符,然后,父进程用来写管道,子进程用来读管道。
 .当读一个写端已经被关闭的管道时,在所有数据被读完后,read返回0。
 .当写一个读端关别的管道时,产生信号SIGPIPE,write返回-1,errno设置为EPIPE。
 .在写管道时PIPE_BUF规定了内核中管道缓冲区的大小,如果多个进程同时写一个管道(或FIFO)其中有一个进程要求写入的字节数超过了该常量,那么写操作的数据可能相互穿插。
 .还要注意,对于管道来说I/O函数是全缓存的。比如对于函数:fgets,fputs等。

 1.2 管道的应用
  管道一般用在有共同祖先的进程间进行协同工作。典型的应用是shell的各命令间用管道相连接,形成一个流水线作业,还可以利用管道实现一个令牌环状的结构,后面会有例子给出。
  
例子1:

/* apue 中的第一个管道的例子 */

#include "apue.h"

int
main(void)
{
    int n;
    int fd[2];
    pid_t pid;
    char line[MAXLINE];
    
    if(pipe(fd) < 0){   /* 先建立管道得到一对文件描述符 */
        exit(0);
    }
    if((pid = fork()) < 0)  /* 父进程把文件描述符复制给子进程 */
        exit(1);
    else if(pid > 0){     /* 父进程写 */
        close(fd[0]);     /* 关闭读描述符 */
        write(fd[1], "\nhello world\n", 14);
    }
    else{           /* 子进程读 */
        close(fd[1]);  /* 关闭写端 */
        n = read(fd[0], line, MAXLINE);
        write(STDOUT_FILENO, line, n);
    }
    exit(0);
}

 
例子2
  这个例子更常用一点,利用管道的时候一般的做法是把标准输出和输入端重定向到管道,这样就可以调用一些标准的I/O函数了。例如:

#include "all.h"

static int do_pipe(char *str);
//static int do_pipe2(char *str);

int
main(int argc, char *argv[])
{
    char *str = "this is a test!\n";

    return (do_pipe(str));
}


static int do_pipe(char *str)
{
    int pfd[2];
    pid_t pid;
    char buf;    

    assert(str);
    
    if (pipe(pfd) == -1) {
        perror("pipe");
        exit(FAIL);
    }
        
    if ((pid = fork()) < 0) {
        perror("fork");
        exit(FAIL);
    }

    if (pid == 0) {    /* child: read */
        close(pfd[1]);
        /*
          * Here, we must check.
          * If pfd[0] == STDIN_FILENO, we will close both of them,
          * since they are the same fd.
          */
        if (pfd[0] != STDIN_FILENO) {
            /*
              * If fd1 is equal to fd2, dup2(fd1,fd2) do nothing.
              */
            if (dup2(pfd[0], STDIN_FILENO) < 0) {
                perror("dup2");
                exit(FAIL);
            }
            close(pfd[0]);
        }
        /*
          * 这里作了重定向后,就可以使用标准的I/O函数了,比如:fgets等
          */
        /* 法1 : while (read(STDIN_FILENO, &buf, 1) > 0) */
        while ((buf = getchar()) > 0)
            write(STDOUT_FILENO, &buf, 1);
        _exit(0);
    } else {    /* parent: write */
        close(pfd[0]);
        write(pfd[1], str, strlen(str));    
        close(pfd[1]);
        wait(NULL);
    }
    
    return OK;
}

 
1.3 流水线管道
   流水线管道向这样 stdin->proc1-->(==管道=)->proc2->stdout
   就是由进程1从标准输入中读入数据,数据通过管道,发送到进程2,进程2接受到数据后把数据输出到标准输出。这样就形成了一个流水线, 就向命令 ls -l | sort 一样。下面是两个进程组成的流水线,当然可以是多个进程的。
   例子:

/*
 * do_pipe2.c
 *
 * Function : ls -l | sort
 */

#include "all.h"

int
main(void)
{
    pid_t cpid;
    int fd[2];
    int master;
    char name[1024];
    
    if (pipe(fd) == -1)
        exit(0);
    if ((cpid = fork()) < 0) {
        perror("fork()");
        exit(0);
    }
       
    if (cpid > 0) { //parent
        close(fd[0]);
        if (fd[1] != STDOUT_FILENO) {
            if (dup2(fd[1], STDOUT_FILENO) == -1) {
                perror("dup2()");
                exit(0);
            }
            close(fd[1]);
        }
        if (execl("/bin/ls", "ls", "-l", NULL) < 0) {
            perror("execl()");
        }
    //wait(NULL);
    exit(0);
    } else if (cpid == 0) { /* child read */
        close(fd[1]);
        if (fd[0] != STDIN_FILENO) {
            if (dup2(fd[0], STDIN_FILENO) == -1) {
                perror("dup2()");
                exit(0);
            }
            close(fd[0]);
        }
         /*
          * Sort default default FILE is stdin.
          * So child will block until parent closed pipe write end.
          */

        if (execl("/bin/sort", "sort", NULL) < 0)
            perror("execl()");
        _exit(0);
    }
    
    return OK;
}


注意:这里如何把child的命令改成less将会出现问题。
 
1.4 协同进程
  所谓协同进程就是:一个进程和一个外部程序协作完成一项任务。比如说让外部程序把从终端输出的字符串全部变成大写,等等。

参考书籍
<>
<>

阅读(1689) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~