Chinaunix首页 | 论坛 | 博客
  • 博客访问: 36036
  • 博文数量: 20
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 22
  • 用 户 组: 普通用户
  • 注册时间: 2013-11-01 10:40
文章分类

全部博文(20)

文章存档

2014年(20)

我的朋友

分类: LINUX

2014-04-27 19:05:19

无名管道:管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)。

    单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。

    数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。(有点像队列哈)

#include

int pipe(int fd[2])

    该函数创建的管道的两端处于一个进程中间,在实际应用中没有太大意义,因此,一个进程在由pipe()创建管道后,一般再fork一个子进程,然后通过管道实现父子进程间的通信(因此也不难推出,只要两个进程中存在亲缘关系,这里的亲缘关系指的是具有共同的祖先,都可以采用管道方式来进行通信)。

    向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。

注:只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIFPIPE信号,应用程序可以处理该信号,也可以忽略(默认动作则是应用程序终止)。

2)有名管道:不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信(能够访问该路径的进程以及FIFO的创建进程之间),因此,通过FIFO不相关的进程也能交换数据。值得注意的是,FIFO严格遵循先进先出(first in first out),对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。它们不支持诸如lseek()等文件定位操作。

有名管道的创建

#include

#include

int mkfifo(const char * pathname, mode_t mode)

    该函数的第一个参数是一个普通的路径名,也就是创建后FIFO的名字。第二个参数与打开普通文件的open()函数中的mode 参数相同。如果mkfifo的第一个参数是一个已经存在的路径名时,会返回EEXIST错误,所以一般典型的调用代码首先会检查是否返回该错误,如果确实返回该错误,那么只要调用打开FIFO的函数就可以了。一般文件的I/O函数都可以用于FIFO,如closereadwrite等等

3)无名管道由一个在基本文件系统存储设备上的INODE,一个与其相连的内存INODE,两个打开文件控制块(分别对应管道的信息发送端和信息接收端)及其所属进程的描述信息来标识,在系统执行PIPEP)命令行之后生成。并在P[0]中返回管道的读通道打开文件描述等,在P[1]中返回管道的写通道打开文件描述符。从结构上看,无名管道没有文件路径名,不占用文件目录项,因此文件目录结构中的链表不适用于这种文件,它只是存在于打开文件结构中的一个临时文件,随其所依附的进程的生存而生存,当进程终止时,无名管道也随之消亡。

送入管道的信息一旦被读进程取用就从管道中消失了,读写操作之间符合先进先出的队列原则。

    管道文件是进程间通信的工具,为了尽量少的占用系统存储资源,一般系统均将其限制为最大长度为4096PIPSIZ)字节的小型文件。当欲写入的消息超过4096字节时,就产生了读、写进程之间的同步问题。首先写操作查找PIPE文件中当前指针的偏移量F-OFFSET,然后从此位置开始尽量写入信息,当长度达到4096字节时,系统控制写进程进入睡眠状态,一直等待读进程取走全部信息时,文件长度指针置0,写进程才被唤醒继续工作。

    为防止多个进程同时读写一个管道文件而产生混乱,在管道文件的INODE标志字I-FLAY项中设置了ILOCK标志项,以设置软件锁的方式实现多进程间对管道文件的互斥使用。

无名管道存在着如下两个严重的缺点。

    第一,无名管道只能用于连接具有共同祖先的进程。

    第二,无名管道是依附进程而临时存在的。所以后来推出了一种无名管道的变种-有名管道,它常被称为FIFO。有名管道除继承了无名管道的所有特性优点之外,还屏弃了无名管道的两个缺点。

    首先,FIFO是一种永久性的机构,它具有普通的UNIX系统文件名。在系统下可利用MKNOD命令建立永久的管道,除非刻意删除它,否则它将一直保持在系统中。

其次,正是由于有名管道以“文件名”来标识,所以只要事先约定某一特定文件名,那样所有知道该约定的服务进程,不论它们之间是否有亲属关系,都可以便利地利用管道进行通信。

    通过下面的命令可以创建一个命名管道:

    /etc/mknod pipe_name p 

    其中“pipe_name”是要创建的命名管道的名字,参数p 必须出现在命名管道名字之后。 

    命名管道文件被创建后,一些进程就可以不断地将信息写入命名管道文件里,而另一些进程也可以不断地从命名管道文件中读取信息。对命名管道文件的读写操作是可以同时进行的。下面的例子显示命名管道的工作过程。

    进程ABC中运行的程序只是一条简单的echo命令,它们不断地把信息写入到命名管道文件/tmp/pipe1中。与此同时,程序中的“read msg”命令不断地从命名管道文件/tmp/pipe1中读取这些信息,从而实现这些进程间的信息交换。 

    程序执行时,首先创建命名管道文件,此时程序处于等待状态,直到ABC进程中某一个进程往命名管道中写入信息时,程序才继续往下执行。使用rm命令可以删除命名管道文件从而清除已设置的 命名管道。 

 

例子:fifo_write向管道中写入数据,fifo_read从管道中读取数据

//fifo_write.cpp

#include unistd.h>
#include sys/types.h>
#include sys/wait.h>
#include stdio.h>
#include stdlib.h>
#include errno.h>
#include math.h> 
#include string.h>
#include fcntl.h>

#define FIFO_SERVER "/tmp/myfifo"

int main(int argc,char **argv)
{
 int fd;
 char w_buf[100];
 int nwrite;
 //创建有名管道
 if((mkfifo(FIFO_SERVER,O_CREAT|O_EXCL|O_RDWR)<0)&&(errno!=EEXIST))
  {
   printf("cannot create fifoserver\n");
   }
   
 //打开管道
 fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);
 if(fd==-1)
  {
   perror("open");
   exit(1);
   }
   
 //入参检验
 if(argc==1)
  {
   printf("please send something\n");
   exit(-1);
   }
 strcpy(w_buf,argv[1]);
 
 //向管道写数据
 if((nwrite=write(fd,w_buf,100))==-1) //fd文件描述符
  {
   if(errno==EAGAIN)
    printf("the FIFO has not been read yet,please try later\n");
    
   } 
   else
    {
     printf("write %s to the FIFO\n",w_buf);
     } 
   close (fd);//关闭管道
   return 0;
 
 }

 

//fifo_read.cpp

#include unistd.h>
#include sys/types.h>
#include sys/wait.h>
#include stdio.h>
#include stdlib.h>
#include errno.h>
#include math.h> 
#include string.h>
#include fcntl.h>

#define FIFO"/tmp/myfifo"

int main(int argc,char **argv)
{
 int fd;
 char buf_r[100];
 int nread;           
 
 printf("Preparing for reading bytes...\n");
 memset(buf_r,0,sizeof(buf_r));     
 
 //打开管道
 fd=open(FIFO,O_RDONLY|O_NONBLOCK,0);
 if(fd==-1)
  {
   perror("open");
   exit(1);
   }
   
   
 while(1)
 {
  memset(buf_r,0,sizeof(buf_r));
  
  if((nread=read(fd,buf_r,100))==-1)
   {
    if(errno=EAGAIN)
     printf("no data yet\n");
    }
  printf("read %s from FIFO\n",buf_r);
  sleep(1);
  
  }
  close(fd);//关闭管道
  pause();//暂停等待信号
  unlink(FIFO);//删除文件
 }

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