linux之进程间通信——管道
Posted 努力学习的少年
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux之进程间通信——管道相关的知识,希望对你有一定的参考价值。
目录
进程间是怎么通信的
之前我们说过,进程与进程之间是相互独立的,它们的数据也是绝对独立的,那么如果一个进程要向另一个进程发送数据,就需要借助第三方资源,这个第三方资源必须让这两个进程都能看到,且通信的时候,互不影响对方,在内核中,有几种方式能够实现进程通信,今天要讲的是管道
管道
所谓的管道,是内存中的一个缓存文件,进程A在管道写入数据,实际是写入到内核中的缓存文件中,进程B从管道中读取数据,实际是从内核的缓存文件中读取数据。
管道的传输数据是单向的,只能由一个进程写入,一个进程读出。
匿名管道
概念
匿名管道是没有名字的,它是特殊文件,它只创建在内存中,不存在我们磁盘中,即用完就销毁了。#include<unistd.h>
int pipe(int fd[2]);
功能:在内核中创建一个匿名管道
参数:
fd:文件描述符数组,其中将f[0]存储读端的文件描述符,fd[1]存储写端的文件描述符。
返回值:成功返回0,失败返回-1.
匿名管道一般适用于有血缘关系之间的通信,例如父子进程,兄弟进程 等
匿名管道是如何实现进程间通信
1.父进程利用pipe创建出一个匿名管道,得到的文件表示符指向管道的读端和写端
2.利用fork生成一个子进程,子进程的文件描述符也指向管道的读端和写端。
3.关闭父进程的读端和关闭子进程的写端 或者 关闭父进程的写端和关闭子进程的读端。
这样匿名管道就创建完成。
ps aux | grep myfile
我们知道“ | ”这也是一个管道,它其实也是一个匿名管道,上面这条命令是将ps 进程输出的数据输送到管道中,然后将grep从管道中读取数据,并查找myfile文件,然后将结果打印到屏幕上
内核角度理解匿名管道
当父进程创建子进程的时候,系统会给子进程创建file_struct和file的数据结构,然后将父进程的file_struct和file的中数据拷贝给子进程的的file_struct和file,这样父进程和子进程就能够指向同一个管道。
代码:
管道的读写规则
情景1
让子进程一直往管道中写入数据,父进程的读端不关闭,但不读取数据。
结果
刚开始,子进程一直往管道中写入数据,当管道写满之后,则子进程就不会往管道中写入数据,子进程就被阻塞等待,等父进程来读取数据时,子进程才会被运行起来。如图所示:
那么我们既然知道管道是有大小的,我们来测试一下我这个版本的linux管道大小是多少。
此时的父进程还是在睡眠,不读取数据。
输出结果:
当输出到65536的时候,则不在屏幕上打印数据,则说明子进程往管道写入的数据已满,所以我这个版本的linux的匿名管道的大小为65536个字节。
情景2
当子进程往管道中写入一定的数据后,就不再往管道中写入数据,并让父进程一直读取管道中的数据。
子进程
父进程:
当子进程只往管道中写入数据时,而父进程不断的往管道中读取数据,当父进程将管道中的数据读完之后,则父进程就会被阻塞等待,直到子进程往管道中写数据时,父进程才会读取管道中的数据。
情景3
当子进程往管道中写完数据后就关闭写端,父进程一直在读取.
结果:
子进程往管道中写入三条数据,同时父进程将这三条数据给读取上来,子进程关闭写端后,父进程去读取数据时,read读不到数据,并返回0。
情景4
当子进程一直往管道中写数据,父进程休息5秒后关闭读端。
结果:
前5秒上显示器打了5条 i am child,直到第5秒,父进程的读端关闭后,子进程就被系统给杀掉了,不在打印i am child。
总结:
1.当管道中没数据的时候,读端的进程就会被阻塞等待(前提写端没有关闭)。
2.当管道中的数据被写满的时候,写端就会被阻塞等待(前提读端没有关闭)。
3.当写端关闭的时候,读端把数据读完之后,则在读取数据时,read会返回0.
4.当读端关闭的时候,写端的进程会自动的被操作系统给杀掉。
5.半双工通信,只能有一端写入,一端读取。
命名管道
匿名管道是通过子进程能够继承父进程的pcb,file和file_struct等结构体,使父子进程(兄弟进程等有血缘关系的进程)能够看到同一个管道,可是两个没有任何的关系进程是没办法这种方式看到同一个管道的。然而系统有命名管道可以让这两个不同的进程看到同一个管道,实现通信。
我们可以在磁盘中创建一个管道文件,当要通信的时候,不同的进程以不同的读或写的方式往磁盘上打开这个管道文件,然后将这个管道文件加载到缓存内核中,然后一个进程往内核中的管道文件中写数据,另一个进程就往内核中的管道文件中读数据,原理是跟匿名管道是类似的。
命名管道是存在磁盘上,它是有文件名的,命名管道让不同的进程在同一路劲下打开命名管道,然后进行通信,它们在内存所写的数据不会刷新到磁盘上。它的存在只是为了让进程能够通过路径找到这个管道文件,然后实现通信。
创建命名管道
命令行方式创建
mkfifo pipe
在磁盘上创建一个名字为pipe的命名管道。
命名管道的文件属性是p。
函数创建
int mkfifo(const char *filename,mode_t mode);
filename:创建管道的文件名
mode:管道的权限属性
返回值:成功返回0,失败返回-1.
命名管道的应用场景
场景1
利用命名管道实现一个进程对另一个进程的通信
思路:client进程从键盘读取到的数据传输到命名管道上,然后server进程再去从命名管道中读取数据,然后将信息打印到屏幕上。
头文件:
makefile文件:
client.c文件:
server.c文件
运行结果:
场景2
一个进程利用命名管道给另一个进程输入某个指令,让该进程执行该指令。
client.c文件
server.c文件
运行结果:
场景3
用命名管道实现文件拷贝
思路:进程A读取myfile文件的内容,然后将这些数据写到管道中,进程B创建出一个新文件myfile_1,然后进程B从管道中读取数据并写入到新文件myfile_1中,直到结束。
client.c文件
server.c 文件
管道的特点:
1.一般而言,进程退出,管道释放,管道的生命周期是随进程的。
2.一般而言,内核会对管道操作进行同步和互斥。
3.管道是半双工的,只能有一个方向流动,需要双方通信的时候,是会建立起两个管道。
好啦,今天的内容就分享到这里,喜欢的读者给个三连呗,感谢你的支持。
以上是关于linux之进程间通信——管道的主要内容,如果未能解决你的问题,请参考以下文章