进程间通信笔记(2)—管道和FIFO
来源:互联网 发布:java ringbuffer 编辑:程序博客网 时间:2024/05/16 01:27
1.概述
管道(pipe):局限在于没有名字,只能用于亲缘关系的进程使用。
FIFO:称为有名管道(named pipe)
2.客户-服务器例子
跟套接字编程的套路类似,客户-服务器回射程序:
这里客户从标准输入(stdin)读入一个路径名,并把它写入IPC通道。服务器打开文件,读出其中内容,并写入IPC通道作为对客户的响应;客户将服务器回射来的内容打印即写到标准输出(stdout)。
3.管道
#include <unistd.h>int pipe(int fd[2]);
pipe
函数创建管道,并提供单向的数据流(不是全双工哦,如果需要双向数据流则需要创建两个管道),该函数返回两个文件描述符:fd[0]
和fd[1]
。前者用来读,后者用来写。
管道的典型应用是用于父子进程的通信,父进程创建一个管道后调用fork派生出自己的副本,接着父进程关闭管道的读出端,子进程关闭写入端。这样父子进程间就有了一个单向的数据流,如下示意:
另外,在shell中使用“|”作为管道命令符,例如:
cat /etc/issue | grep Ubuntu
在两个进程之间创建了一个管道,通过管道,前一个进程的标准输出传递给下一个进程作为标准输入。
3.1示例
下面实现一个经典的管道示例,父进程和子进程之间完成通信,在主程序中创建两个管道,用于双向通信。父进程为客户,子进程为服务器,第一个管道用于客户向服务器发送路径名,第二个管道用于服务器向客户发送该文件内容:
3.2代码
通过创建两个管道实现全双工的通信:父进程从标准输入读入文件的路径名并写入管道,子进程从管道中读出文件名并打开文件,读出文件中的内容写入管道,父进程从管道中接收文件中的内容并写入到标准输出。
为了方便学习,也是把书中的代码解包裹。。。
#include <stdio.h>#include <errno.h>#include <stdlib.h>#include <string.h>#include <sys/wait.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>void server(int ,int);void client(int ,int);const int MAXLINE = 1024;int main(int argc,char ** argv){ int pipe1[2]; int pipe2[2]; pid_t childpid; //创建两个管道 if(pipe(pipe1)<0) printf("pipe1 error\r\n"); if(pipe(pipe2)<0) printf("pipe2 error\r\n"); if((childpid = fork())==0) //子进程 { close(pipe1[1]);//关闭写 close(pipe2[0]);//关闭读 server(pipe1[0],pipe2[1]); return 0; } //父进程 close(pipe1[0]);//关闭读 close(pipe2[1]);//关闭写 client(pipe2[0],pipe1[1]); wait(NULL); return 0;}void client(int readfd,int writefd){ size_t len; ssize_t n; char buff[MAXLINE]; //读入路径名 if(fgets(buff,MAXLINE,stdin)==NULL) { printf("fgets error\r\n"); return ; } len=strlen(buff); if(buff[len-1]=='\n') len--; //路径名写入管道 if(write(writefd,buff,len)<0) { printf("write error\r\n"); return ; } //接收文件中数据,写入到标准输出 while((n=read(readfd,buff,MAXLINE))>0) write(STDOUT_FILENO,buff,n);//STDOUT_FILENO 1 // STDOUT_FILENO=fileno(stdout);}void server(int readfd,int writefd){ int fd; ssize_t n; char buff[MAXLINE]; if((n=read(readfd,buff,MAXLINE))<=0) { printf("read error\r\n"); return ; } buff[n]='\0'; fd=open(buff,O_RDONLY); if(fd < 0) { snprintf(buff+n,sizeof(buff)-n,": can't open, %s\r\n",strerror(errno)); n=strlen(buff); write(writefd,buff,n); }//open error 错误信息返回 else { //从文件中读入数据并写入管道 while((n=read(fd,buff,MAXLINE))>0) { write(writefd,buff,n); } close(fd); }}
4.FIFO
FIFO类似于管道,它是一个单向数据流,不同的是,FIFO有一个路径名与之关联,从而允许无亲缘关系的进程访问同一个FIFO,FIFO也被称为有名管道(named pipe),使用mkfifo
函数可以创建。
#include <sys/types.h>#include <sys/stat.h>int mkfifo(const char * pathname,mode_t mode);//成功返回0 出错返回-1
不仅仅是函数,我们可以在shell下使用mkfifo创建有名管道。
例如:
xxx@xxx: mkfifo mypipexxx@xxx: echo helloworld > mypipe
在另一终端:
xxx@xxx: read line < mypipexxx@xxx: echo $line
使用fifo让两个无亲缘关系的进程进行通信,由于每个fifo有一个路径名与之关联,因此创建fifo后,需要使用I/O函数打开读或者打开写。
NOTE:fifo不能打开来既读又写,因为它是半双工的。也就是说O_RDWR这种打开模式将是未定义的
示例代码
在使用mkfifo
函数的时候,需要注意一些问题:
1.mkfifo是隐含O_CREAT | O_EXCL也就是说,该函数要么创建一个FIFO(成功),要么返回一个EEXIST错误(失败,该FIFO已经存在),对于后者来说,它并不妨碍我们继续使用这个已经存在FIFO进行通信。
2.关于创建的FIFO(所关联的那个文件),需要说明读写权限,书中给了一个默认权限:允许用户读写、组内成员和其他用户读,使用掩码的方式包括起来就是:
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
下面就可以实现一个简单的生产者消费者了,简单起见,就只创建一个FIFO完成半双工的通信了。
生产者
//fifowrite.c#include <stdio.h>#include <errno.h>#include <stdlib.h>#include <string.h>#include <sys/wait.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>int main(int argc,char** argv){ const char * const PATHNAME = "/home/zhangxiao/zxtest/pipe/myfifo";//关联的路径名 const char * const BUFF = "Fifo Write Test.\r\n"; int fd; if( (mkfifo(PATHNAME,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)<0) && (errno != EEXIST) ) { printf("can't create %s\r\n",PATHNAME); return -1; } fd = open(PATHNAME,O_WRONLY, 0); write(fd,BUFF,strlen(BUFF)); return 0;}
消费者
//fiforead.c#include <stdio.h>#include <errno.h>#include <stdlib.h>#include <string.h>#include <sys/wait.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>int main(int argc,char** argv){ const int MAXLEN=1024; const char * const PATHNAME = "/home/zhangxiao/zxtest/pipe/myfifo";//关联的路径名 ssize_t n; int fd; char readbuff[MAXLEN]; memset(readbuff,0x00,sizeof(readbuff));//初始化 if( (mkfifo(PATHNAME,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)<0) && (errno != EEXIST) ) { printf("can't create %s\r\n",PATHNAME); return -1; } fd = open(PATHNAME,O_RDONLY, 0); while( (n = read(fd,readbuff,sizeof(readbuff)))>0 ) { write(STDOUT_FILENO,readbuff,n);//标准输出 } return 0;}
5.参考
1.《UNP卷2》
2.http://stackoverflow.com/questions/25900873/write-and-read-from-a-fifo-from-two-different-script
- 进程间通信笔记(2)—管道和FIFO
- 【进程间通信】普通管道和命名管道(FIFO)
- UNP(卷2:进程间通信)—— 第4章:管道和FIFO
- 进程间通信 管道和FIFO
- 进程间通信之管道和FIFO
- 4. 卷2(进程间通信)---管道和FIFO
- 进程间的通信—管道pipe和fifo
- UNIX进程间通信(1. 管道和FIFO)
- Linux环境进程间通信(一)管道和FIFO
- Linux环境进程间通信(一)管道和FIFO
- UNPv2:进程间通信(二) 管道和FIFO
- 进程间通信--命名管道(fifo)
- 进程间通信——有名管道(FIFO)
- 2、进程间通信-FIFO(有名管道)
- Linux--进程间通信(管道及有名管道FIFO)
- Linux--进程间通信(管道及有名管道FIFO)
- Linux--进程间通信(管道及有名管道FIFO)
- 进程间通信--管道、命名管道、FIFO
- Stanford机器学习__Lecture notes CS229. Linear Regression(3)
- 测试Atlas的分表功能
- 利用手摇法实现原地归并排序
- Sourceinsight最佳配色方案及颜色字体调整方法
- 浅谈TCP/IP协议栈(七)网络层常见协议汇总
- 进程间通信笔记(2)—管道和FIFO
- UML 时序图
- Java基础知识点整理Day_03
- 艺术风格转换之《A Neural Algorithm of Artistic Style》
- Java设计模式之组合模式
- C++ 玩转素数(前方高能哦)
- C和指针编程练习---6.18.1
- ProtoStuff序列化工具
- java8 Arrays.sort 与Arrays.parallelSort