[TOC]
(来自:http://blog.chinaunix.net/uid-26833883-id-3227144.html)
一、什么是管道?
管道是单向的、先进先出的,它把一个进程的输出和另一个进程的输入连接在一起。一个进程(写进程)在管道的尾部写入数据,另一个进程(读进程)从管道的头部读出数据。
二、管道的分类
管道包括无名管道和命名管道两种,前者用于父进程和子进程间的通信,后者可用于运行于同一系统中的任意两个进程间的通信。
三、管道特点
- 管道通讯是单向的,有固定的读端和写端。
- 数据被进程从管道读出后,在管道中该数据就不存在了。
- 当进程去读取空管道的时候,进程会阻塞。
- 当进程往满管道写入数据时,进程会阻塞。
- 管道容量为64KB(#define PIPE_BUFFERS 16
include/linux/pipe_fs_i.h)
在Linux系统中,无名管道一旦创建完成后,操作无名管道
等同于操作文件。无名管道的读端被视作一个文件;无名管
道的写端也被视作一个文件
四、无名管道操作函数pipe
-
创建无名管道pipe函数 头文件: 《unistd.h》 函数原型: int pipe(int pipefd[2]) ; 函数功能: 创建一个可用于进程间通信的无名管道; 函数参数: pipefd[0]:读段文件描述符;pipefd[1]:写端文件描述符; 函数返回值: 成功:返回0;失败:返回-1; 注意: 这无名管道通信的操作方式其实也跟操作文件一样。父子进程对管道分别有自己的读写通道,把无关的读端或写段关闭。 无名管道的读写规则探究
- A.从管道中读取数据
<1>写端不存在时,此时则认为已经读到了数据的末尾,读函数返回的读出字节数为0;
<2>写端存在时,如果请求的字节数目大于PIPE_BUF(ubuntu操作系统为65536),则返回管道中现有的数据字节数,如果请求的字节数目不大于PIPE_BUF,则放回管道中现有数据字节数(此时,管道中数据量小于请求的数据量);或者返回请求的字节数(此时,管道中数据量不小于请求的数据量)
从以上验证我们可以看到:
<1>当写端存在时,管道中没有数据时,读取管道时将阻塞
<2>当读端请求读取的数据大于管道中的数据时,此时读取管道中实际大小的数据
<3>当读端请求读取的数据小于管道中的数据时,此时放回请求读取的大小数据; - 向管道中写入数据:
向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。当管道满时,读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。 - 注意:只有管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIGPIPE信号,应用程序可以处理该信号,也可以忽略(默认动作则是使应用程序终止)。
探究发现,当管道数据满时,此时再向管道写数据,写端将阻塞。当读端不存在时,写端写数据,内核将向其发送SIGPIPE信号,默认是终止进程。
- A.从管道中读取数据
#include <unistd.h>
#include <sys/wait.h>
int main( int argc , char **argv ) {
int fd[2] ;
int ret = pipe(fd) ;
if( 0>ret )
printf( "creat pipe fail!\n" ) ;
pid_t pid = fork() ;
if( pid>0 ) {
/*父子进程对管道分别有自己的读写通道,把无关的读端或写段关闭。*/
wait(NULL) ;
close( fd[1] ) ;
printf(" father start read \n") ;
char buf[5] = {0} ;
read( fd[0] , buf , 5 ) ;
printf( "father read 1 is : %s\n",buf ) ;
strcpy( buf,"") ;
read( fd[0] , buf , 5 ) ;
printf( "father read 2 is : %s \n",buf ) ;
close(fd[0]) ;
exit(0) ;
}else{
/*父子进程对管道分别有自己的读写通道,把无关的读端或写段关闭。*/
printf("this is son write .\n") ;
close( fd[0]) ;
write( fd[1] , "good" , 5 ) ;
close( fd[1] ) ;
printf( "son write succeed \n" ) ;
exit(0) ;
}
return 0 ;
}