简介:
管道是UNIX系统IPC最古老形式,并且所有UINX系统都提供此种通信机制。
普通的Linux shell都允许重定向,而重定向使用的就是管道。例如:ls | grep *.txt.管道是单向的、先进先出的、无结构的、固定大小的字节流,它把一个进程的标准输出和另一个进程的标准输入连接在一起。写进程在管道的尾端写入数据,读进程在管道的道端读出数据。数据读出后将从管道中移走,其它读进程都不能再读到这些数据。管道提供了简单的流控制机制。进程试图读空管道时,在有数据写入管道前,进程将一直阻塞。同样,管道已经满时,进程再试图写管道,在其它进程从管道中移走数据之前,写进程将一直阻塞。管道主要用于不同进程间通信。
管道分无名管道(pipe)和有名管道(FIFO)。
无名管道(pipe):
创建一个无名管道可以使用系统调用pipe()。它接受一个参数,也就是一个包括两个整数的数组。如果系统调用成功,此数组将包括管道使用的两个文件描述符。创建一个管道之后,一般情况下进程将产生一个新的进程。
系统调用:pipe();原型:int pipe(int fd[2]);返回值:如果系统调用成功,返回0。如果系统调用失败返回-1:errno=EMFILE(没有空亲的文件描述符)EMFILE(系统文件表已满)EFAULT(fd数组无效)注意:fd[0]用于读取管道,fd[1]用于写入管道。
无名管道的读写:
管道主要用于不同进程间通信。实际上,通常先创建一个管道,再通过fork函数创建一个子进程。图见附件。
子进程写入和父进程读的命名管道。
无名管道读写注意事项:
可以通过打开两个管道来创建一个双向的管道。但需要在子理程中正确地设置文件描述符。必须在系统调用fork()中调用pipe(),否则子进程将不会继承文件描述符。当使用半双工管道时,任何关联的进程都必须共享一个相关的祖先进程。因为管道存在于系统内核之中,所以任何不在创建管道的进程的祖先进程之中的进程都将无法寻址它。而在命名管道中却不是这样。
无名管道示例:
#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));数组中的数据清0;
if(pipe(pipe_fd)<0){
printf("pipe create error\n");
return -1;
}
if((pid=fork())==0){
printf("\n");
close(pipe_fd[1]);
sleep(2);
if((r_num=read(pipe_fd[0],buf_r,100))>0){
printf("%d numbers read from be pipe is %s\n",r_num,buf_r);
}
close(pipe_fd[0]);
exit(0);
}else if(pid>0){
close(pipe_fd[0]);
if(write(pipe_fd[1],"Hello",5)!=-1)
printf("parent write success!\n");
if(write(pipe_fd[1]," Pipe",5)!=-1)
printf("parent wirte2 succes!\n");
close(pipe_fd[1]);
sleep(3);
waitpid(pid,NULL,0);
exit(0);
}
}
标准流管道:
与linux中文件操作有文件流的标准I/O一样,管道的操作也支持基于文件流的模式。popen函数实现的操作是:创建一个管道,调用fork产生一个子进程,关闭管道不使用端,执行一个shell以运行命令,然后等待命令终止。
接口函数如下:库函数:popen();原型:FILE *open (char *command,char
*type);返回值:如果成功,返回一个新的文件流。如果无法创建进程或者管道,返回NULL。管道中数据流的方向是由第二个参数type控制的。此参数可以是r或者w,分别代表读或写。但不能同时为读和写。在Linux 系统下,管道将会以参数type中第一个字符代表的方式打开。所以,如果你在参数type中写入rw,管道将会以读的方式打开。
使用popen()创建的管道必须使用pclose()关闭。其实,popen/pclose和标准文件输入/输出流中的fopen()/fclose()十分相似。
库函数:pclose();原型:int pclose(FILE *stream);返回值:返回系统调用wait4()的状态。如果stream无效,或者系统调用wait4()失败,则返回-1。注意此库函数等待管道进程运行结束,然后关闭文件流。库函数pclose()在使用popen()创建的进程上执行wait4()函数,它将破坏管道和文件系统。
流管道的例子。
#include#include
#include
#include
#define BUFSIZE 1024
int main(){
FILE *fp;
char *cmd="ps -ef";
char buf[BUFSIZE];
buf[BUFSIZE]='\0';
if((fp=popen(cmd,"r"))==NULL)
perror("popen");
while((fgets(buf,BUFSIZE,fp))!=NULL)
printf("%s",buf);
pclose(fp);
exit(0);
}
命名管道(FIFO):
命名管道与无名管道基本相同,但也有一些显著的不同:
A、命名管道是在文件系统中作为一个特殊的设备文件而存在的。
B、不同祖先的进程之间可以通过管道共享数据。
C、当共享管道的进程执行完所有的I/O操作以后,命名管道将继续保存在文件系统中以便以后使用。
无名管道只能由相关进程使用,它们共同的祖先进程创建了管道。但是,通过FIFO,不相关的进程也能交换数据。
命名管道创建
#include
#include
int mkfifo(const char *pathname,mode_t
mode);
返回:若成功则为0,若出错返回-1
一旦已经用mkfifo创建了一个FIFO,就可用open打开它。确实,一般的文件I/O函数(close,read,write,unlink等)都可用于FIFO。当打开一个FIFO时,非阻塞标(O_NONBLOCK)产生下列影响:
(1)在一般情况中(没有说明O_NONBLOCK),只读打开要阻塞到某个其他进程为写打开此FIFO。类似,为写而打开一个FIFO要阻塞到某个其他进程为读而打开它。
(2)如果指一了O_NONBLOCK,则只读打开立即返回。但是,如果没有进程已经为读而打开一个FIFO,那么只写打开将出错返回,其errno是ENXIO。类似于管道,若写一个尚无进程为读而打开的FIFO,则产生信号SIGPIPE。若某个FIFO的最后一个写进程关闭了该FIFO,则将为该FIFO的读进程产生一个文件结束标志。
FIFO相关出错信息:
EACCES(无存取权限)EEXIST(指定文件不存在)ENAMETOOLONG(路径名太长)ENOENT(包含的目录不存在)ENOSPC(文件系统余空间不足)ENOTDIR(文件路径无效)EROFS(指定的文件存在于只读文件系统中)
有名管道示例:
fifo_write.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_read.c#include
#include
#include
#include
#include
#include
#include
#define FIFO_SERVER "/tmp/myfifo"
main(int argc,char** argv)
{
int fd;
char w_buf[100];
int nwrite;
if(fd==-1)
if(errno==ENXIO)
printf("open error;no reading process\n");
fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);
if(argc==1)
printf("Please send something\n");
strcpy(w_buf,argv[1]);
if((nwrite=write(fd,w_buf,100))==-1)
{
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);
}