Chinaunix首页 | 论坛 | 博客
  • 博客访问: 821592
  • 博文数量: 92
  • 博客积分: 1498
  • 博客等级: 上尉
  • 技术积分: 993
  • 用 户 组: 普通用户
  • 注册时间: 2009-09-18 18:31
文章分类

全部博文(92)

文章存档

2013年(2)

2012年(3)

2011年(3)

2010年(61)

2009年(23)

分类: LINUX

2010-08-14 00:33:42

过几天就要给linux兴趣小组的同学讲下我最近学的一些东西,我觉的还是进程间通信这块是学习linux C的一个亮点吧,进程间通信的手段很多。其中管道以及有名管道划分一类,还有信号,消息队列(又称报文队列),共享内存,信号量,套接口等。

1.管道的特点总结起来也就是下面几点:

  • 半双工,即数据只能向一个方向流动,需要双方通信时,需要建立两个管道
  • 仅用于父子进程和兄弟进程之间
  • 单独的一个特殊的文件系统,只存在于内存当中
  • 特殊的数据读出和写入规则:pipe一个管道后 [0]为读出端,[1]为写入端

2.管道的创建:pipe()函数,原型为

#include
int pipe(int pipefd[2]);
3.管道的使用(读写相关):
管道创建后就可以使用,进程间通信,所以,是pipe创建管道后,然后fork下,父子进程共同继承这个管道的信息,于是就有了父子进程间信息的交流,当然,你也可以自己给自己发消息(使用管道,不过这没什么意义,貌似)。
前面说过,pipe创建一个位于内存中的管道后,文件描述符fd[0]和fd[1]分别就是读端和写端(名字随意,也可以是pipe_fd[0],pipe_fd[1],关键是这个索引,0和1),这是规定,这样用就行了。可以这样记忆,pipe产生的管道0端读,1端写。
管道中读数据的时候:如果没写端,默认到了数据末尾,函数返回读出数据字节数为0;有写端,那么要读取的字节数大于PIPE_BUF(这个定义于include/linux/limits.h中,我的内核定义为4096),则返回管道中现有的所有数据字节数。如果不大于PIPE_BUF返回管道中现有的数据字节数,或者返回请求的数据字节数。
下面是一个读规则的测试。

/*
 * Copyright (c) 2010-~ zhouyongfei
 *
 * The source code is released for free distribution under the terms of the GNU General Public License
 *
 *
 * Author: alen Chou
 * Created Time: 2010年08月13日 星期五 11时01分35秒
 * File Name: readtest.c
 * Description:
 *
 */

 
#include
#include
#include
#include
int main(int argc, char *argv[])
{
 
 int pipe_fd[2];
 pid_t pid;
 char r_buf[100];
 char w_buf[50];
 int r_num;
 
 memset(r_buf,0,sizeof(r_buf));
 memset(w_buf,0,sizeof(r_buf));
 
 if(pipe(pipe_fd) < 0){ printf("pipe create error\n"); return -1; } if(0 == (pid=fork())){ printf("\n"); close(pipe,pipe_fd[1]);/*子进程关闭写端*/ sleep(3); /*确保父进程关闭写端*/ r_num = read(pipe_fd[0],r_buf,100); printf("read num is %d ,the data read from the pipe is \"%s\"\n",r_num,r_buf); close(pipe_fd[0]);/*子进程关闭读端*/ exit(0); }else if(pid > 0){
  close(pipe_fd[0]);/*父进程关闭读端*/
  strcpy(w_buf,"hello ,this is alen Chou….");
  if(write(pipe_fd[1],w_buf,sizeof(w_buf)) != -1)
   printf("parent write over\n");
  close(pipe_fd[1]);/*父进程关闭写端*/
  printf("parent close fd[1] over\n");
  sleep(5);
  exit(0);
 }
 return 0;
}

从上面的例子可以简单的看出,读端每次先关闭写端,读取数据完成后关读端,写端每次先关闭读端,写完后关闭写端,你也可以不完全这样,但是会出现这样或者那样的问题,因为管道是半双工,所以每次必须保证一端写,另一端读,这样才能保证数据传输的正确性。
管道中写数据时:linux不保证写数据的原子性,管道缓冲区有空闲区,写进程就会尝试写入,如果管道已满,而没有读进程读取管道缓冲区的数据,那么写进程将会一直阻塞直到有读进程读走数据。
下面是一个测试写规则的例子:

/*
 * Copyright (c) 2010-~ zhouyongfei
 *
 * The source code is released for free distribution under the terms of the GNU General Public License
 *
 *
 * Author: alen Chou
 * Created Time: 2010年08月13日 星期五 11时59分57秒
 * File Name: pipe3.c
 * Description:
 *
 */

 
#include
#include
#include
#include
#include
#include
 
main(int argc,char**argv)
{
 int stat;
 
 int pipe_fd[2];
 pid_t pid;
 char r_buf[4096];
 char w_buf[4096*2];
 int writenum;
 int rnum;
 memset(r_buf,0,sizeof(r_buf));
 if(pipe(pipe_fd)<0){ printf("pipe create error\n"); return -1; } if((pid=fork())==0){ close(pipe_fd[1]); /*while(1){ sleep(1); rnum=read(pipe_fd[0],r_buf,1000); printf("child: readnum is %d\n",rnum); if(rnum == 0){ break; } //sleep(1); }*/ close(pipe_fd[0]); exit(0); } else if(pid>0){
  //close(pipe_fd[0]);//write

  memset(r_buf,0,sizeof(r_buf));
 
  if((writenum=write(pipe_fd[1],w_buf,5000))==-1)
   printf("write to pipe error\n");
  else
   printf("the bytes write to pipe is %d \n", writenum);
 
  if((writenum=write(pipe_fd[1],w_buf,5000))==-1){
   printf("write to pipe error\n");
  }else
   printf("the bytes write to pipe again is %d\n",writenum);
  close(pipe_fd[1]);
  wait(&stat);
 }
 return;
}


这个例子中有许多值得琢磨的地方,比如可以看到写操作原子性的规则,以及写操作的时候的阻塞问题,测试程序的时候可以在不同的地方加上注释就会出现不同的结果。
4.最后说下管道的一些局限性:

  • 只支持单向数据流;
  • 只能用于具有亲缘关系的进程之间;
  • 没有名字;
  • 管道的缓冲区是有限的
  • 管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式,比如多少字节算作一个消息等。

当然上面的一些问题我们现在就可以解决,比如半双工只能是数据单向流动,那我们可以创建两个管道,然后fork父子进程,这样就可以使用两个管道进行相互的数据传输了。有些问题就得留给后面去解决,比如解决非亲缘关系的进程间的信息传输就可以使用有名管道等

好了,管道就说道这了。。

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