7.1.2 管道的应用

       管道是利用pipe()系统调用而不是利用open()系统调用建立的。pipe()调用的原型是:

 

        int pipe(int fd[2])

 

     我们看到,有两个文件描述符与管道结合在一起,一个文件描述符用于管道的read()端,一个文件描述符用于管道的write()端。由于一个函数调用不能返回两个值,pipe()的参数是指向两个元素的整型数组的指针,它将由调用两个所要求的文件描述符填入。

fd[0]元素将含有管道read()端的文件描述符,而fd[1]含有管道write()端的文件描述符。系统可根据fd[0]fd[1]分别找到对应的file 结构。在第8章我们会描述pipe()系统调用的实现机制。

 注意,在pipe的参数中,没有路径名,这表明,创建管道并不象创建文件一样,要为它创建一个目录连接。这样做的好处是,其它现存的进程无法得到该管道的文件描述符,从而不能访问它。那么,两个进程如何使用一个管道来通信呢 ?

我们知道,fork()exec()系统调用可以保证文件描述符的复制品既可供双亲进程使用,也可供它的子女进程使用。也就是说,一个进程用pipe()系统调用创建管道,然后用fork()调用创建一个或多个进程,那么,管道的文件描述符将可供所有这些进程使用。pipe()系统调用的具体实现将在下一章介绍。

这里更明确的含义是:一个普通的管道仅可供具有共同祖先的两个进程之间共享,并且这个祖先必须已经建立了供它们使用的管道。

注意:在管道中的数据始终以和写数据相同的次序来进行读,这表示lseek()系统调用对管道不起作用。

下面给出在两个进程之间设置和使用管道的简单程序:

 

#include <stdio.h>

#include <unistd.h>

#include <sys/types.h>

int main(void)

{

        int     fd[2], nbytes;

        pid_t   childpid;

        char    string[] = "Hello, world!\n";

       char    readbuffer[80];

 

       pipe(fd);

 

        if((childpid = fork()) == -1)

        {

                printf("Error:fork");

                exit(1);

        }

 

        if(childpid == 0)        /* 子进程是管道的写进程 */

       {

               close(fd[0]);      /*关闭管道的读端  */

               write(fd[1], string, strlen(string));

               exit(0);

        }

        else                           /* 父进程是管道的读进程  */

        {

                   close(fd[1]);    /*关闭管道的写端 */

               nbytes = read(fd[0], readbuffer, sizeof(readbuffer));

                printf("Received string: %s", readbuffer);

        }       

        return(0);

}

 

注意,在这个例子中,为什么这两个进程都关闭它所不需的管道端呢?这是因为写进程完全关闭管道端时,文件结束的条件被正确地传递给读进程。而读进程完全关闭管道端时,写进程无须等待继续写数据。

阻塞读和写分别成为对空和满管道的默认操作,这些默认操作也可以改变,这就需要调用fcntl()系统调用,对管道文件描述符设置O_NONBLOCK标志可以忽略默认操作:

 

# include  <fcntl.h>

 

fcntl(fd,F_SETFL,O_NONBlOCK);