管道内部如何实现-大小,组织方式,环形队列?
一.进程间通信有多种方式,本文主要讲解对管道的理解。管道分为匿名管道和命名管道。
(1)管道( pipe ):又称匿名管道。是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
(2)命名管道 (named pipe或FIFO) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
二.管道
1. 管道的特点:
(1)管道是半双工的,数据只能向一个方向流动;双方通信时,需要建立起两个管道;
(2)只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);
(3)单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。
(4)数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。
2.通信步骤
2.1创建管道
利用pipe()函数创建管道,即在内核中开辟一块缓冲区(称为管道)用于通信。pipe函数调用成功返回0,调用失败返回-1。
管道是基于文件描述符的通信方式。当一个管道建立时,它会创建两个文件描述符fd[0]和fd[1]。其中fd[0]固定用于读管道,而fd[1]固定用于写管道,一般文件I/O的函数都可以用来操作管道(lseek除外)。管道的读写规则如下:
从管道中读取数据:
如果管道的写端不存在,则认为已经读到了数据的末尾,读函数返回的读出字节数为0;
当管道的写端存在时,如果请求的字节数目大于PIPE_BUF,则返回管道中现有的数据字节数,如果请求的字节数目不大于PIPE_BUF,则返回管道中现有数据字节数(此时,管道中数据量小于请求的数据量);或者返回请求的字节数(此时,管道中数据量不小于请求的数据量)。注:(PIPE_BUF在include/linux/limits.h中定义,不同的内核版本可能会有所不同。Posix.1要求PIPE_BUF至少为512字节,red hat 7.2中为4096)。
向管道中写入数据:
向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。
注:只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIFPIPE信号,应用程序可以处理该信号,也可以忽略(默认动作则是应用程序终止)。
2.2创建子进程
单独创建一个无名管道,并没有实际的意义。我们再fork一个子进程,然后通过管道实现父子进程间的通信(即两个进程中存在亲缘关系,这里的亲缘关系指的是具有共同的祖先)。
2.3父进程关闭管道读端,子进程关闭管道写端
3.代码验证
3.1检验管道大小
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<unistd.h> 4 int main() 5 { 6 int fd[2]; 7 int count=0; 8 if(pipe(fd)<0) 9 { 10 perror("Fail to create pipe"); 11 exit(EXIT_FAILURE); 12 } 13 while(1) 14 { 15 write(fd[1],"a",sizeof(char)); 16 printf("count=%d.\n",++count); 17 } 18 return 0; 19 }
运行结果:65536=64K
3.2读写规则探究
A.从管道中读取数据
<1>写端不存在时
1 #include<stdio.h> 2 #include<unistd.h> 3 #include<stdlib.h> 4 int main() 5 { 6 int n; 7 int fd[2]; 8 int count=0; 9 char buf[100]={0}; 10 11 if(pipe(fd)<0) 12 { 13 perror("Fail to create pipe"); 14 exit(EXIT_FAILURE); 15 } 16 close(fd[1]); 17 18 if((n=read(fd[0],buf,sizeof(buf)))<0) 19 { 20 perror("Fail to read pipe"); 21 exit(EXIT_FAILURE); 22 } 23 printf("read %d bytes:%s\n",n,buf); 24 return 0; 25 }
运行结果:read 0 bytes:
<2>写端存在时
父进程向管道中写数据,子进程从管道中读取数据
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #define N 10 #define MAX 100 int child_read_pipe(int fd) { char buf[N]; int n = 0; while(1) { n = read(fd,buf,sizeof(buf)); buf[n] = ‘\0‘; printf("Read %d bytes : %s\n",n,buf); if(strncmp(buf,"quit",4) == 0) break; } return 0; } int father_write_pipe(int fd) { char buf[MAX] = {0}; while(1) { printf(">"); fgets(buf,sizeof(buf),stdin); buf[strlen(buf)-1] = ‘\0‘; write(fd,buf,strlen(buf)); sleep(1); if(strncmp(buf,"quit",4) == 0) break; } return 0; } int main() { int pid; int fd[2]; if(pipe(fd) < 0) { perror("Fail to pipe"); exit(EXIT_FAILURE); } if((pid = fork()) < 0) { perror("Fail to fork"); exit(EXIT_FAILURE); } else if(pid == 0) { close(fd[1]); child_read_pipe(fd[0]); } else { close(fd[0]); father_write_pipe(fd[1]); } exit(EXIT_SUCCESS); }
本文出自 “sunshine225” 博客,请务必保留此出处http://10707460.blog.51cto.com/10697460/1762775
进程间通信IPC—匿名管道(pipe)和命名管道(fifo)
原文地址:http://10707460.blog.51cto.com/10697460/1762775