Chinaunix首页 | 论坛 | 博客
  • 博客访问: 270136
  • 博文数量: 84
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 927
  • 用 户 组: 普通用户
  • 注册时间: 2015-03-06 23:00
个人简介

growing

文章分类

全部博文(84)

文章存档

2017年(6)

2016年(61)

2015年(17)

我的朋友

分类: LINUX

2016-07-02 11:58:30

一、匿名管道pipe

特点:
1.用于有血缘关系的进程通讯(继承其文件描述符)
2.两个进程通过一个管道只能实现单向通信
3.流式服务
4.生命周期:随进程
5.提供同步、互斥

看一个例子:

  1. #include<stdio.h>
  2. #include<errno.h>
  3. #include<sys/wait.h>
  4. #include<unistd.h>
  5. #include<string.h>
  6. #include<sys/types.h>

  7. int main()
  8. {
  9.     int pipe_fd[2] = {-1,-1};
  10.     if(pipe(pipe_fd) < 0){
  11.         printf("pipe error, %s\n",strerror(errno));
  12.         return 1;
  13.     }

  14.     pid_t id = fork();
  15.     if(id == 0){
  16.         //child write
  17.         close(pipe_fd[0]);

  18.         char *msg = "child write:enenshiwo\n";
  19.         while(1){
  20.             write(pipe_fd[1],msg,strlen(msg));
  21.             sleep(1);
  22.         }
  23.     }else{
  24.         //father read
  25.         close(pipe_fd[1]);

  26.         char buf[1024];
  27.         while(1){
  28.             buf[0] = '\0';
  29.             ssize_t _sz = read(pipe_fd[0],buf,sizeof(buf) - 1);

  30.             if(_sz > 0){
  31.                 buf[_sz] = '\0';
  32.             }

  33.             printf("father read : %s\n",buf);
  34.         }
  35.         wait(NULL);
  36.     }
  37.     return 0;
  38. }
子进程关闭读端来写,父进程关闭写端来读。

在阻塞IO操作下,有以下四种情况:

1.没有人在写、写的文件描述符都关闭了,依然有人读,read会把剩余的读完后返回0
2.没有人写、写的文件描述符没关闭,一直有人读,read会阻塞,等待管道里有了数据再继续读并返回。

3.没有人读,读的文件描述符都关闭了,依然有人写,那么写的进程会收到信号SIGPIPE而异常终止
4.没有人读,读的文件描述符没关闭,依然有人写,等写满了管道后write会阻塞,直到有人读,管道也可以写进去才写入并返回。

可以想象是下水管道的一个,哪一段出了问题另一端都会跟着反应,总不能把管子撑爆、也不可能等着不会再有水的管道来水吧。

二、命名管道 FIFO

特点:
1.创建FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO 相互通信
2.先进先出,like its name.(FIFO)frist in frist out
3.当使用FIFO的进程退出,FIFO文件会继续保存在文件系统中以便以后使用。
4.FIFO中的内容放在内存中

看一个例子:
client:
  1. #define _PATH_ "./file.tmp"
  2. #define _SIZE_ 100

  3. int main()
  4. {
  5.     int fd = open(_PATH_,O_RDONLY,0);
  6.     if(fd < 0){
  7.         printf("open file failed!:%s\n",strerror(errno));
  8.         return 1;
  9.     }
  10.     char buf[_SIZE_];
  11.     while(1){
  12.         memset(buf,'\0',sizeof(buf));
  13.         int ret = read(fd,buf,sizeof(buf)-1);
  14.         if(ret <= 0){
  15.             printf("read file failed!\n");
  16.             break;
  17.         }
  18.         printf("%s\n",buf);
  19.         if(strncmp(buf,"quit",4) == 0){
  20.             break;
  21.         }
  22.     }
  23.     close(fd);
  24.      return 0;
  25. }
server

  1. #define _PATH_ "./file.tmp"
  2. #define _SIZE_ 100

  3. int main()
  4. {
  5.     int ret = mkfifo(_PATH_,0666|S_IFIFO);
  6.     if(ret == -1){
  7.         printf("mkfifo failed!\n");
  8.         return 1;
  9.     }
  10.     int fd = open(_PATH_,O_WRONLY|O_CREAT,0644);
  11.     if(fd < 0){
  12.         printf("open error:%s\n",strerror(errno));
  13.     }
  14.     char buf[_SIZE_];
  15.     while(1){
  16.         printf("Server Enter:");
  17.         fflush(stdout);
  18.         memset(buf,'\0',sizeof(buf));
  19.         scanf("%s",buf);
  20.         int len = strlen(buf);
  21.         buf[len] = '\0';
  22.         int ret = write(fd,buf,strlen(buf));
  23.         if(ret < 0){
  24.             printf("write error!:%s\n",strerror(errno));
  25.             break;
  26.         }
  27.         if(strncmp(buf,"quit",4) == 0){
  28.             break;
  29.         }
  30.     }
  31.     close(fd);
  32.     return 0;
  33. }
server is write one
client is read one

三、管道的容量

一次管道读写操作最多传送512字节, linux/limits.h中规定:
#define PIPE_BUF  4096

那么当写入超过buf会怎样,我们来man一下:
Writes  of greater than {PIPE_BUF} bytes may have data interleaved(交织), on arbitrary(任意) boundaries, with writes by other processes, whether or not the O_NONBLOCK flag of the  file  status  flags  is set.

If  the  O_NONBLOCK  flag is clear, a write request may cause the thread to block, but on normal completion it
shall return nbyte.

ssize_t write(int fildes, const void *buf, size_t nbyte);

总结一下:
写一个大于管道总容量的数据量,write无论阻塞还是非阻塞都会返回,对于阻塞的,需要有人read把空间让出来,直到nbyte全部写入才返回,对于 非阻塞的,write会尽量写入并且立即返回,并给给予一个保证就是如果pipe在此刻是空的,那么write起码会写入PIPE_SIZE字节。

四、管道的实现(这里参考:这里)
  1.环形缓冲区  
  每个管道只有一个页面作为缓冲区,该页面是按照环形缓冲区的方式来使用的。这种访问方式是典型的“生产者——消费者”模型。当“生产者”进程有大量的数据需 要写时,而且每当写满一个页面就需要进行睡眠等待,等待“消费者”从管道中读走一些数据,为其腾出一些空间。相应的,如果管道中没有可读数据,“消费者” 进程就要睡眠等待,

2.环形缓冲区实现原理

环形缓冲区是嵌入式系统中一个常用的重要数据结构。一般采用数组形式进行存储,即在内存中申请一块连续的线性空间,可以在初始化的时候把存储空间一次性分配 好。只是要模拟环形,必须在逻辑上把数组的头尾相连接。只要对数组最后一个元素进行特殊的处理——访问尾部元素的下一元素时,重新回到头部元素。对于从尾 部回到头部只需模缓冲长度即可(假设maxlen为环形缓冲的长度,当读指针read指向尾部元素时,只需执行read=read%maxlen即可使read回到头部元素)

3.读写操作

环形缓冲区要维护写端(write)和读端(read)两个索引。写入数据时,必须先确保缓冲区没有满,然后才能将数据写入,最后将write指针指向下一个元素;读取数据时,首先要确保缓冲区不为空,然后返回read指针对应得元素,最后使read指向下一个元素的位置。

4.判断“满”和“空”

readwrite指向同一个位置时环形缓冲区为空或满。为了区别环满和空,当readwrite重叠的时候环空;而当writeread快,追到距离read还有一个元素间隔的时候,就认为环已经满了。


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