管道
管道是队列型数据结构,常见的应用是连接两个进程的输入输出管道包括无名管道和有名管道,前者在父子进程中流行,后者由于可独立成磁盘文件形式存在,能够被无血缘关系的进程共享。
无名管道——管道pipe
占用文件描述符,不能被非亲缘的进程共享,一般用于父子进程,UNIX系统中一切皆为文件,管道也是文件的一种,称为管道文件。
当系统创建一个管道时,他返回两个文件描述符:一个文件以只写打开,作为管道的输入端;另一个文件以只读打开,作为管道的输出端
使用API
- 创建——pipe(int arr[2]);
- 关闭——正常文件操作
匿名管道的描述
- 在内核开辟缓冲区
- (ubuntu 16.04)大小为4096Bytes
- 数据结构为环型队列
- 管道具有方向性
- 管道内可交互数据与信息
管道使用流程
- 父进程完成管道的创建(子进程继承) pipe(int fds[2])
- 如果管道创建成功,内核将管道的访问描述符传出到 数组参数中
- 父进程可以通过两个文件描述符访问读写管道
- 创建管道成功后创建子进程(子进程继承两个文件描述符(管道的访问句柄))
- 使用匿名管道时[确定通信方向,父写子读]
- 管道使用结束,正确关闭释放管道
注:f[0] 管道输出端,用于进程读,f[1] 管道输入端用于写
管道使用注意特殊情况
- 管道读端关闭,写端向管道写入数据——内核向写端进程发送SIGPIPE(杀死·写端进程)
- 如果写端关闭读端读取管道——读端读到0(表示写端关闭)
- 读端未读取管道数据,写端写满管道后再次写——写端阻塞
- 写端未向管道写数据,读端读取数据——读端阻塞
匿名管道优缺点
- 优点:匿名管道体积小,速度快,这样方便
- 缺点:只能完成亲缘进程(父子)之间的通信,默认情况下传输无格式字节流,匿名管道使用单工通信方式
注:单工通信(非读即写)、半双工通信(非读即写可切换单工通信)、全双工通信(同时读写)
注:注:在进程通信中,无法判断每次通信中报文的字节数,即无法对数据流进行自动拆分,从而发生子进程一次性读取父进程两次通信的报文情况。为实现正常拆分发送报文,采用如下方法:
固定长度
显式长度
短连接
有名管道FIFO
FIFO管道,有名的管道,以一种特殊的文件类型存储于文件系统中,供无血缘关系进程访问。
命令创建 mkfifo
有名管道API
#include <sys/types>
#include <sys/stat.h>
int mkfifo(char *path, mode_t mode); // 创建文件
mode : S_IFIFO|0666
函数mkfifo创建有名管道,字符串path指定管道文件的路径和名称,参数mode决定了管道文件的访问权限。
管道本身就是文件,所有文件操作适合于管道文件,可按以下步骤应用管道:
创建管道文件;
读进程:只读打开管道文件,读管道;
写进程:只写打开管道文件,写管道;
关闭管道文件(应用函数close或fclose)
注:虽然无名管道没有路径,但仍为文件,占据i节点和数据块;
无名管道没有路径,所以只能通过继承方式操作;
无名管道一旦关闭,就不能再次使用;
如管道一次性写入小于PIPE_BUF字节的数据,该操作为原子操作,否则拆分再发送;
有名管道使用流程
- 创建管道文件[命令创建:mkfifo names] 函数创建 mkfifo( const char * names , int mod)
- 读写端进程打开管道文件【读写端以不通的方式打开管道文件,区分读写访问】
- 打开成功后,读写端进程可以持续读写交互数据
- 使用完成后,读写端进程关闭文件描述符,如果希望销毁释放管道缓冲区,需要删除管道文件
有名管道使用时注意
成功打开管道文件,必须用有读写打开方式如果只有一种,例如只都打开的进程,那么open阻塞,等待只写打开的进程,如果一个进程在打开管道文件时,以读写方式打开,则打开成功且不会产生open阻塞