按有无名称,管道主要分为有名管道和匿名管道
管道是一种进程之间进行单向通信的方式,由于其通信只是单方向的,所以起有以下缺点:
1.通信只是单方向的,通信太局限
2.其缓冲区大小是一定的,缓冲区满了之后就无法继续再写入数据。
3.通过管道传输的只能是无格式的字节流。
4.只能用于具有亲缘关系的进程之间,如父子进程,兄弟进程。
管道创建使用函数pipe,其原型为:
#include<unistd.h> intpipe(int pipefd[2]);
函数参数pipefd[2]为整形的数组,可作为一般的文件描述符使用,管道读端用pipefd[0]表示,管道写端用pipefd[1]表示,创建成功,则返回0,失败返回-1,管道创建后,就可以
作为一般的文件对待,对一般的I/O操作同样适用,如read,write等。
请看示例:
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<sys/types.h> #include<unistd.h> void write_pipe(int fd) //管道读端 { char *message="hello pipe test\n"; if(write(fd,message,strlen(message)+1)) //向管道读端写入数据,使用write就可以 printf("write success\n"); } void read_pipe(int fd) //管道写端 { char *message; message=(char *)malloc(100*sizeof(char)); if(read(fd,message,100)) //从管道读端读取数据 { printf("get pipe:%s\n",message); } else { printf("get message fail\n"); } free(message); } int main() { int fd[2]; //定义一个int型的二维数组,作为pipe函数的参数 int stat_val; pid_t pid; if(pipe(fd)) //创建管道 { printf("make a pipe fail\n"); return 0; } pid=fork(); //创建子进程 switch(pid) { case 0: //子进程用于读 close(fd[1]); read_pipe(fd[0]); exit(0); case -1: printf("fork a new process fial"); exit(0); default: //父进程用于写 close(fd[0]); write_pipe(fd[1]); wait(&stat_val); exit(0); } return 1; }
执行结果:
writesuccess
getpipe:hello pipe test
顾名思义,有名管道就是拥有名字的管道,当然,他与匿名管道的区别就是他拥有了名字,与此同时,他也克服了匿名管道的部分缺陷与不足,如它可以在任意两个进程之间进
行通信,当然也只能局限于单向的。
用于创建管道的函数有两个分别为:mknod和mkfifo,这两个函数原型分别为;
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> intmknod(const char *pathname, mode_t mode, dev_t dev);
说明:pathname指的是创建的有名管道的全文件路径名,mode为创建的有名管道的模式,既存权限,一般都为S_IFIFO|0666,dev指的是设备值,该值取决创建的文件的种
类,只有在创建设备文件时才用得到。函数调用成功反或0失败返回-1
#include<sys/types.h> #include<sys/stat.h> intmkfifo(const char *pathname, mode_t mode);
说明:pathname指的是创建的有名管道的全文件路径名,mode为创建有名管道的模式,既存权限,一般都为S_IFIFO|0666
使用mknod创建有名管道:
umask(0); if(mknod(“fifo”,S_IFIFO|0666)) { perror(“mkfifo error!”); exit(0); }
使用mkfifo创建有名管道:
umask(0); if(mkfifo(“fifo”,S_IFIFO|0666)) { perror(“mkfifo error !”); exit(0); }
说明:创建管道时,要特别注意,pathname必须是你有读写权限的路径,因为管道实质上就是一些特殊的文件,管道创建时自动创建,结束后自动删除。还有关于mode,必
须在前面使用umask修饰,表面上权限是由mode决定的,但真正权限是mode&~umask得到的。还有,有名管道在进行读写时,得像文件一样先打开,读写完毕后关闭。
//processread.c #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/stat.h> #include<fcntl.h> #include<sys/types.h> int main() { char buf[1024]; int fd; umask(0); fd=open("myfifo",O_RDONLY); //打开名为myfile的管道名,路径为当前路径 read(fd,buf,1024); <span style="white-space:pre"> </span>//读取管道里的内容 printf("read :%s\n",buf); //输出管道里的内容 close(fd); exit(0); } //processwrite.c #include<stdio.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<string.h> #include<stdlib.h> #include<unistd.h> int main() { int fd; char buf[40]="hello world,this message send by fifo !"; umask(0); if(mkfifo( "myfifo" , S_IFIFO|read :hello world,this message send by fifo ! 0666)==-1) /*有名创建管道*/ { printf("create new fifo fail!\n"); exit(1); } if((fd=open("myfifo",O_WRONLY))==-1) //先打开管道文件 { printf("open fail !\n"); exit(1); } write(fd,buf,strlen(buf)+1); <span style="white-space:pre"> </span>//向管道里面写入信息 close(fd); exit(0); }
程序说明:
将两个文件分别编译后,先运行processwrite,这时候,读端由于对写端的依赖,次进程会阻塞在终端,打开另一终端,运行processread,神奇的结果会出现;
read:hello world,this message send by fifo !
许多人会想到,既然管道是单向的,那么两条管道岂不是就可以实现互相通信了嘛!是的,下面例子就是实现两个进程之间互相通信:
//service.c #include<stdio.h> #include<unistd.h> #include<fcntl.h> #include<sys/types.h> #include<sys/stat.h> #include<stdlib.h> #include<string.h> #include<errno.h> #define BUF_SIZE 1024 #define FIFO_WRITE "writefifo" //服务端写管道名 #define FIFO_READ "readfifo" //服务端读管道名 int main() { errno=0; int wfd,rfd; char buf[BUF_SIZE]; int len; umask(0); if(mkfifo(FIFO_WRITE,S_IFIFO|0666)) //创建一个有名管道 { printf("Create %s fail because:%s",FIFO_WRITE,strerror(errno)); exit(1); } umask(0); wfd=open(FIFO_WRITE,O_WRONLY); //打开管道写端 if(wfd==-1) { printf("Open %s errno,because :%s",FIFO_WRITE,strerror(errno)); exit(1); } while((rfd=open(FIFO_READ,O_RDONLY))==-1) //打开管道读端 { sleep(1); //如果打开失败,沉睡一秒,重新读取 } while(1) { printf("Service:"); fgets(buf,BUF_SIZE,stdin); //获取用户的输入 buf[strlen(buf)-1]='\0'; //字符串要以‘\0‘结尾 if(strncmp(buf,"quit",4)==0) //若字符串以quit开始,则退出 { close(wfd); unlink(FIFO_WRITE); //断开连接 close(rfd); exit(0); } write(wfd,buf,strlen(buf)); //向通道中写入东西 len=read(rfd,buf,BUF_SIZE); //从通道中获取信息 if(len>0) { buf[len]='\0'; printf( "Client :%s\n",buf ); } } } //client.c #include<stdio.h> #include<sys/types.h> #include<fcntl.h> #include<sys/stat.h> #include<string.h> #include<stdlib.h> #include<errno.h> #include<unistd.h> #define BUF_SIZE 1024 #define FIFO_READ "writefifo" /*客户端写管道名,这里要特别注意,宏定义名与实际有反差*/ #define FIFO_WRITE "readfifo" //客户端读管道名,这里也要注意 int main() { int rfd,wfd; char buf[BUF_SIZE]; int len; errno=0; umask(0); if( mkfifo( FIFO_WRITE, S_IFIFO|0666 ) ) //创建有名管道 { printf("creat fifo %s fail,because:%s ",FIFO_WRITE, strerror(errno)); exit(1); } while((rfd=open(FIFO_READ,O_RDONLY))==-1) //打开管道读端 { sleep(1); } wfd=open(FIFO_WRITE, O_WRONLY); //打开管道写端 if(wfd==-1) { printf("Fail to open %s ,because: %s",FIFO_WRITE,strerror(errno)); exit(-1); } while(1) { len=read(rfd,buf,BUF_SIZE); //读取管道里的信息 if(len>0) { buf[len]='\0'; printf("Service:%s\n",buf); } printf("Client :"); fgets(buf,BUF_SIZE,stdin); buf[strlen(buf)-1]='\0'; if(strncmp(buf,"quit",4)==0) { close(wfd); unlink(FIFO_WRITE); //断开连接 close(rfd); //关闭文件 exit(0); } write(wfd,buf,strlen(buf)); //写入数据 } }
程序说明:
分别编译后,首先运行service,后打开另一个终端运行client,然后就可以互相通信了。
效果图: