匿名管道
管道是一种最基本的进程间通信方式,由pipe函数在内核中创建一块缓冲区用于通信,这块缓冲区就是管道,管道有两个端口,一边是读端fd[0]
,另一边是写端fd[1]
,用户程序从管道中读写数据。
- pipe函数
#include <unistd.h>
int pipe(int fd[2]);
管道实现进程通信方式
1.父进程创建管道
父进程调用pipe开辟管道,得到两个文件描述符指向管道的两端。
2.父进程fork()
出子进程
父进程调⽤用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。
3.父进程关闭fd[0]
,子进程关闭fd[1]
父进程关闭管道读端,子进程关闭管道写端。父进程可以往管道里写子进程可以从管道里读,管道是用环形队列实现的,数据从写端流入从读端流出,这样就实现了进程间通信。
代码示例
- 代码执行结果
管道特点
1 只能进行单行通信
2 管道通信依赖于文件系统,当进程退出时,管道也会随着推出,所以管道的生命周期称为随进程
3 进程共享文件描述符才能通信,所以通信仅限于有血缘关系的进程之间的通信,(常用于父子进程)
4 管道在进行数据通信时,按”流“的方式进行通信,这里的”流“指的是数据流
5 管道内部自带同步机制
管道通信时四种情况
1.如果所有指向管道写端的文件描述符都关闭了(管道写端的引用计数等于0),而仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样
2.如果有指向管道写端的文件描述符没关闭(写端的引用计数大于0)而持有管道写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read 会阻塞,直到管道中有数据可读了才读取数据并返回。
3.如果所有指向管道读端的文件描述符都关闭了(管道读端的引用计数等于0),这时有进程向管道的写端write,那么会收到信号SIGPIPE,通常会导致进程异常中止。
4.如果有指向管道读端的文件描述符没关闭(管道读端的引用计数大于0),而持有读端的进程也没有从管道中读数据。这时有进程向管道写端写数据,那么在管道被写满时再次write会阻塞,直到管道中有空位置了才写入数据并返回。
* 注意
管道读写的一定是有效信息
将管道作为一个文件去处理
命名管道 FIFO
—————— 让两个不相干的进程实现通信
管道的一个不足之处是没有名字,因此,只能用于具有亲缘关系的进程间通信,在命名管道(named pipe或FIFO)提出后,该限制得到了克服。FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存储于文件系统中。命名管道是一个设备文件,因此,即使进程与创建FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO相互通信。
FIFO(first input first output)总是按照先进先出的原则工作,第一个被写入的数据将首先从管道中读出。
linux下有两种方式创建命名管道,一是在Shell下交互地建立一个命名管道,二是在程序中使用系统函数建立命名管道。
Shell方式下可使用系统函数mknod()
或mkfifo()
创建命名管道,这两个函数均定义在头文件
#include <sys/types.h>
#include <sys/stat.h>
int mknod(const char *path,mode_t mod,dev_t dev);
int mkfifo(const char *path,mode_t mode);
函数mknod参数中path为创建的命名管道的全路径名:mod为创建的命名管道的模式,指 明其存取权限;dev为设备值,该值取决于⽂文件创建的种类,它只在创建设备⽂文件时才会⽤用 到。这两个函数调⽤用成功都返回0,失败都返回-1。
命名管道和匿名管道区别
* 匿名管道只能让具有亲缘关系的进程通信,而命名管道可以实现不相干的进程之间的通信。
* 命名管道是一个存在于硬盘上的文件,而管道是存在于内存中的特殊文件。