进程间通信【管道】
2018-03-17 22:18
393 查看
一、概念
1、基本概念
管道是一种最基本的IPC机制,作用于两个进程之间,完成数据传递;管道是Unix中最古老的进程间通信的形式。2、管道的特点
(1)只能用于具有共同祖先的进程(具有亲缘关系的进程)之间通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后,父子进程之间就可应用该管道(2)管道的进程间通信是基于字节流的
(3)管道是基于文件形式的,自带同步互斥机制,并且只能进行单向传输
(4)一般而言,进程退出,管道释放,所以管道的生命周期是随进程的
二、管道的分类
1、匿名管道(pipe)
匿名管道用于实现具有亲缘关系的进程间通信,通常用于父子间进程(1)匿名管道的创建函数
注释:调用pipe函数时,首先在内核中开辟一块缓冲区用于通信,它有一个读端和一个写端,然后通过pipefd参数传出给用户进程两个文件描述符,pipefd[0]指向管道的读端,pipefd[1]指向管道的写段。在用户层面看来,打开管道就是打开了一个文件,通过read()或者write()向文件内读写数据,读写数据的实质也就是往内核缓冲区读写数据。
返回值:成功返回0,失败返回-1。
(2)匿名管道实现
那么如何实现父子间进程呢?通常有以下步骤:1. 父进程调用pipe函数创建管道,得到两个文件描述符fd[0]、fd[1]指向管道的读端和写端。
2. 父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。
3. 父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出。由于管道是利用环形队列实现的,数据从写端流入管道,从读端流出,这样就实现了进程间通信。
(3)下面举一个例子说明匿名管道的创建:
mypipe.c1 #include<unistd.h> 2 #include<stdlib.h> 3 #include<error.h> 4 #include<string.h> 5 #include<stdio.h> 6 int main(int argc,char*argv[]) 7 { 8 int pipefd[2]; 9 if(pipe(pipefd)==-1) 10 perror("pipe error"); 11 pid_t pid; 12 pid=fork(); 13 if(pid==-1) 14 perror("fork errpr"); //子进程进行写操作 15 if(pid==0) 16 { 17 close(pipefd[0]); 18 write(pipefd[1],"hello",5); 19 close(pipefd[1]); 20 exit(1); 21 } //父进程进行读操作 22 close(pipefd[1]); 23 char buf[10]={0}; 24 read(pipefd[0],buf,10); 25 printf("buf=%s\n",buf); 26 27 return 0; 28 }
运行结果:
3、读写规则
(1)如果所有指向管道写端的文件描述符都关闭了,而仍然有进程从管道的读端 读数据,那么文件内的所有内容被读完后再次read就会返回0,就像读到文件结尾。(2)如果有指向管道写端的文件描述符没有关闭(管道写段的引用计数大于0),而持有管道写端的进程没有向管道内写入数据,假如这时有进程从管道读端读数据,那么读完管道内剩余的数据后就会阻塞等待,直到有数据可读才读取数据并返回。
(3)如果所有指向管道读端的文件描述符都关闭,此时有进程通过写端文件描述符向管道内写数据时,则该进程就会收到SIGPIPE信号,并异常终止。
(4)如果有指向管道读端的文件描述符没有关闭(管道读端的引用计数大于0),而持有管道读端的进程没有从管道内读数据,假如此时有进程通过管道写段写数据,那么管道被写满后就会被阻塞,直到管道内有空位置后才写入数据并返回。
2、命名管道(fifo)
上面介绍管道应用有两个局限性:a、只能在具有亲缘关系的进程间通信
b、它只能实现一个进程写另一个进程读,而如果需要两者同时进行时,就得重新打开一个管道。
为了解决这一问题,我们可以使用FIFO文件来做这项工作,他经常被称为命名管道,命名管道是一种特殊类型的文件
(1)FIFO文件的两个属性:
1、FIFO是一个设备文件,在文件系统中以文件名的形式存在,因此即使进程与创建FIFO的进程不存在血缘关系也依然可以通信,前提是可以访问该路径。2、FIFO(first input first output)总是遵循先进先出的原则,即第一个进来的数据会第一个被读走。
(2)创建命名管道
(1)直接运用命令创建(2)运用函数接口创建
int mkfifo(const char*filename,mode_mode)
若创建成功返回0,否则返回-1
例子–用命名管道serverPipe与clientPipe之间的通信
serverPipe.c
1 #include<stdio.h> 2 #include<sys/types.h> 3 #include<sys/stat.h> 4 #include<fcntl.h> 5 #include<unistd.h> 6 #include<stdlib.h> 7 8 #define ERR_EXIT(m) \ 9 do{\ 10 perror(m);\ 11 exit(EXIT_FAILURE);\ 12 }while(0) 13 14 int main() 15 { 16 umask(0); 17 if(mkfifo("mypipe",0644)<0) 18 { 19 ERR_EXIT("mkfifo"); 20 } 21 int rfd=open("mypipe",O_RDONLY); 22 if(rfd<0) 23 { 24 ERR_EXIT("open"); 25 } 26 char buf[1024]; 27 while(1) 28 { 29 buf[0]=0; 30 printf("Please wait...\n"); 31 ssize_t s=read(rfd,buf,sizeof(buf)-1); 32 if(s>0) 33 { 34 buf[s-1]=0; 35 printf("client say#%s\n",buf); 36 } 37 else if(s==0) 38 { 39 printf("Client quit,exit now!\n"); 40 exit(EXIT_SUCCESS); 41 } 42 else 43 ERR_EXIT("read"); 44 45 } 46 close(rfd); 47 return 0; 48 }
clientPipe.c
1 #include<stdio.h> 2 #include<sys/types.h> 3 #include<sys/stat.h> 4 #include<fcntl.h> 5 #include<unistd.h> 6 #include<stdlib.h> 7 #include<string.h> 8 9 #define ERR_EXIT(m) \ 10 do{\ 11 perror(m);\ 12 exit(EXIT_FAILURE);\ 13 }while(0) 14 15 int main() 16 { 17 int wfd=open("mypipe",O_WRONLY); 18 if(wfd<0) 19 { 20 ERR_EXIT("open"); 21 } 22 char buf[1024]; 23 while(1) 24 { 25 buf[0]=0; 26 printf("Please Enter# "); 27 fflush(stdout); 28 ssize_t s=read(0,buf,sizeof(buf)-1); 29 if(s>0){ 30 buf[s]=0; 31 write(wfd,buf,strlen(buf)); 32 } 33 else if(s<=0) 34 ERR_EXIT("read"); 35 } 36 close(wfd); 37 return 0; 38 }
makefile
1 #cat Makefile 2 .PHONY:all 3 all:serverPipe clientPipe 4 serverPipe:serverPipe.c 5 gcc -o serverPipe serverPipe.c 6 clientPipe:clientPipe.c 7 gcc -o clientPipe clientPipe.c 8 .PHONY:clean 9 clean: 10 rm -f serverPipe clientPipe 11 ~
运行结果:
实现两个进程间的通信需要我们打开两个终端,便于观察
clientPipe 负责写入数据:
serverPipe负责读数据
(3)命名管道与匿名管道的区别
命名管道创建完成后就可以使用,其使用方法与管道一样;区别在于:
(1)命名管道使用之前需要使用open()打开。这是因为:命名管道是设备文件,它是存储在硬盘上的,而管道是存在内存中的特殊文件。
(2)但是需要注意的是,命名管道调用open()打开有可能会阻塞,但是如果以读写方式(O_RDWR)打开则一定不会阻塞;
(3)以只读(O_RDONLY)方式打开时,调用open()的函数会被阻塞直到有数据可读;
(4)如果以只写方式(O_WRONLY)打开时同样也会被阻塞,知道有以读方式打开该管道。
相关文章推荐
- Linux进程间通信——使用命名管道
- 进程间通信(二):利用匿名管道
- 进程间通信(一)—管道
- Linux环境进程间通信(一)管道和有名管道
- 进程间通信之管道
- 进程间通信之管道
- 1.6 进程间通信方式之有名管道
- UNIX环境高级编程学习之第十五章进程间通信 - 通过匿名管道实现父子进程同步
- 《进程间通信》---------------->管道
- linux内核之进程间通信(1)——管道
- 进程间通信——管道
- 进程间通信--管道
- 进程间通信之管道
- Linux进程间通信(IPC)之二——命名管道(FIFO)
- 进程间通信 - 匿名管道实现
- Linux编程:进程间通信--管道
- 进程间通信——管道代码实现
- 进程间通信之双管道
- 经典进程间通信之无名管道和有名管道
- linux进程间通信之 管道,命名管道,协同进程区别