Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1483012
  • 博文数量: 204
  • 博客积分: 4013
  • 博客等级: 中校
  • 技术积分: 4030
  • 用 户 组: 普通用户
  • 注册时间: 2011-12-29 06:34
文章分类

全部博文(204)

文章存档

2012年(204)

分类: 嵌入式

2012-03-11 03:25:10

管道是 Linux 中进程间通信的一种方式。这里所说的管道主要指无名管道,它具有如下特点:
·  它只能用于具有亲缘关系的进程之间的通信(也就是父子进程或者兄弟进程之间) 。
·  它是一个半双工的通信模式,具有固定的读端和写端
·  管道也可以看成是一种特殊的文件,对于它的读写也可以使用普通的 read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中
 
创建管道
 
调用形式:
#include
int pipe(int fd[2]) 
该函数创建的管道的两端处于一个进程中间,在实际应用中没有太大意义,因此,一个进程在由pipe()创建管道后,一般再fork一个子进程,然后通过管道实现父子进程间的通信
管道的读写规则 :
– 管道两端可分别用描述字fd[0]以及fd[1]来描述,需要注意的是,管道的两端是固定了任务的。即一端只能用于读,由描述字fd[0]表示,称其为管道读端;另一端则只能用于写,由描述字fd[1]来表示,称其为管道写端。
– 如果试图从管道写端读取数据,或者向管道读端写入数据都将导致错误发生。
– 一般文件的I/O函数都可以用于管道,如close、read、write等等。
 
管道读写说明
 

/*管道读写实例pipe_rw.c*/
#include
#include
#include
#include
#include
int main()
{
 int pipe_fd[2];
 pid_t pid;
 char buf_r[100];
 char* p_wbuf;
 int r_num;
 memset(buf_r,0,sizeof(buf_r));
/*创建管道*/
 if(pipe(pipe_fd)<0)
 {
  printf("pipe create error\n");
  return -1;
 } 
/*创建一子进程*/
if((pid=fork())==0){
  printf("\n");
/*关闭子进程描述符,并通过使父进程暂停2秒确保父进程已关闭相应的读描述符*/
  close(pipe_fd[1]); 
  sleep(2); 
/*子进程读取管道内容*/
  if((r_num=read(pipe_fd[0],buf_r,100))>0){ 
   printf("%d  numbers  read  from  the  pipe  is  %s\n",r_num,buf_r);
  }
/*关闭子进程描述符*/
  close(pipe_fd[0]); 
  exit(0);
 }
 else if(pid>0)
   {
/*关闭父进程描述符,并分两次向管道中写入Hello Pipe*/
  close(pipe_fd[0]); 
  if(write(pipe_fd[1],"Hello",5)!= -1)
   printf("parent write1 success!\n");
  if(write(pipe_fd[1]," Pipe",5)!= -1)
   printf("parent write2 success!\n");
/*关闭父进程写描述符*/
  close(pipe_fd[1]);
  sleep(3);
/*收集子进程退出信息*/
  waitpid(pid,NULL,0); 
  exit(0);
  }
}

管道的局限性 (无名管道)

管道的主要局限性正体现在它的特点上:
.只支持单向数据流
.只能用于具有亲缘关系的进程之间;
.没有名字;
.管道的缓冲区是有限的(管道只存在于内存中,在管道创建时,为缓冲区分配一个页面大小);
管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式,比如多少字节算作一个消息(或命令、或记录)等等

FIFO

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

系统调用形式:
#include
#include
int mkfifo(const  char*pathname,mode_t mode)
该函数的第一个参数是一个普通的路径名,也就是创建后FIFO的名字。第二个参数与打开普通文件的open()

函数中的mode参数相同,有以下几种:
O_RDONLY:读管道
O_WRONLY:写管道
O_RDWR:读写管道
O_NONBLOCK:非阻塞
O_CREAT:
O_EXCL:
一般文件的I/O函数都可以用于FIFO,如close、read、write等等。

FIFO读规则

约定:如果一个进程为了从FIFO中读取数据而阻塞打开FIFO,那么称该进程内的读操作为设置了阻塞标志的读操作。
– 如果有进程写打开FIFO,且当前FIFO内没有数据,则对于设置了阻塞标志的读操作来说,将一直阻塞。对于没有设置阻塞标志读操作来说则返回-1,当前errno值为EAGAIN,提醒以后再试。
– 对于设置了阻塞标志的读操作说,造成阻塞的原因有两种:
 A.当前FIFO内有数据,但有其它进程在读这些数据;
 B.另外就是FIFO内没有数据。解阻塞的原因则是FIFO中有新的数据写入,不论信写入数据量的大小,也不论读操作请求多少数据量。
 读打开的阻塞标志只对本进程第一个读操作施加作用,如果本进程内有多个读操作序列,则在第一个读操作被唤醒并完成读操作后,其它将要执行的读操作将不再阻塞,即使在执行读操作时,FIFO中没有数据也一样(此时,读操作返回0)。

如果没有进程写打开FIFO,则设置了阻塞标志的读操作会阻塞。

FIFO写规则

约定:如果一个进程为了向FIFO中写入数据而阻塞打开FIFO,那么称该进程内的写操作为设置了阻塞标志的写操作。
对于设置了阻塞标志的写操作:
– 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果此时管道空闲缓冲区不足以容纳要写入的字节数,则进入睡眠,直到当缓冲区中能够容纳要写入的字节数时,才开始进行一次性写操作。
– 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。FIFO缓冲区一有空闲区域,写进程就会试图向管道写入数据,写操作在写完所有请求写的数据后返回。
对于没有设置阻塞标志的写操作:
– 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。在写满所有FIFO空闲缓冲区后,写操作返回。
– 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果当前FIFO空闲缓冲区能够容纳请求写入的字节数,写完后成功返回;如果当前FIFO空闲缓冲区不能够容纳请求写入的字节数,则返回EAGAIN错误,提醒以后再写;

/*fifl_read.c*/
#include
#include
#include
#include
#include
#include
#include
#define FIFO "/tmp/myfifo"
 
main(int argc,char** argv)
{
 char buf_r[100];
 int  fd;
 int  nread;
/*创建有名管道,并设置相应的权限*/
 if((mkfifo(FIFO,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))
  printf("cannot create fifoserver\n");
 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);
       } 
 pause();
 unlink(FIFO);
}

/*fifo_write.c*/
#include
#include
#include
#include
#include
#include
#include
#define FIFO "/tmp/myfifo"
 
main(int argc,char** argv)
/*参数为即将写入的字节数*/
{
 int fd;
 char w_buf[100];
 int nwrite;
 
/*打开FIFO管道,并设置非阻塞标志*/
 fd=open(FIFO,O_WRONLY|O_NONBLOCK,0);
 if(fd== -1)
  printf("open error; no reading process\n");
 if(argc==1)
  printf("Please send something\n");
 strcpy(w_buf,argv[1]);
/*向管道中写入字符串*/
 if((nwrite=write(fd,w_buf,100))== -1)
       {
       printf("go into write fifo\n");
  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);

 FIFO运行结果



 
 
阅读(4185) | 评论(0) | 转发(1) |
0

上一篇:进程间通信分类

下一篇:pid_t的类型定义

给主人留下些什么吧!~~