Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2149112
  • 博文数量: 361
  • 博客积分: 10828
  • 博客等级: 上将
  • 技术积分: 4161
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-20 14:34
文章分类

全部博文(361)

文章存档

2011年(132)

2010年(229)

分类: C/C++

2010-03-12 10:09:51

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

#include

#include

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);

7.1.3 命名管道(FIFO)

Linux 还支持另外一种管道形式,称为命名管道,或 FIFO,这是因为这种管道的操作方式基于“先进先出”原理。上面讲述的管道类型也被称为“匿名管道”。命名管道中,首先写入管道的数据是首先被读出的数据。匿名管道是临时对象,而 FIFO 则是文件系统的真正实体,如果进程有足够的权限就可以使用 FIFOFIFO 和匿名管道的数据结构以及操作极其类似,二者的主要区别在于,FIFO 在使用之前就已经存在,用户可打开或关闭 FIFO;而匿名管道只在操作时存在,因而是临时对象。

为了创建先进先出文件,可以从shell提示符使用mknod命令或可以在程序中使用mknod()系统调用。

mknod()系统调用的原型为:

 

#include  <sys/type.h>

#inlcude  <sys/state.h>

#include  <fcntl.h>

#include  <unistd.h>

 

int mknod(char *pathname,node_t mode, dev_t dev);

 

其中pathname是被创建的文件名称,mode表示将在该文件上设置的权限位和将被创建的文件类型(在此情况下为S_IFIFO)dev是当创建设备特殊文件时使用的一个值。因此,对于先进先出文件它的值为0

一旦先进先出文件已经被创建,它可以由任何具有适当权限的进程利用标准的open()系统调用加以访问。当用open()调用打开时,一个先进先出文件和一个匿名管道具有同样的基本功能。即当管道是空的时候,read()调用被阻塞。当管道是满的时候,write()等待被阻塞,并且当用fcntl()设置O_NONBLOCK标志时,将引起read()调用和write()调用立即返回。在它们已被阻塞的情况下,带有一个EAGAIN错误信息。

由于命名管道可以被很多无关系的进程同时访问,那么,在有多个读进程/或多个写进程的应用中使用FIFO是非常有用的。

多个进程写一个管道会出现这样的问题,即多个进程所写的数据混在一起怎么办?幸好系统有这样的规则:一个write()调用可以写管道能容纳(Linux4K字节)的任意字节,系统将保证这些数据是分开的。这表示多个写操作的数据在FIFO文件中并不混合而将被维持分离的信息。

 

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