文章目录
Linux管道通信机制
管道是所有Unix及Linux都提供的一种进程间的通信机制,它是进程之间的一个单向数据流,一个进程可向管道写入数据,另一个进程可从管道中读取数据,从而达到数据交换的目的。
Liunx的管道通信机制有无名管道PIPE和有名管道FIFO两种机制。
无名管道
- 无名管道只能用于具有亲缘关系的进程之间的通信。
- 无名管道是半双工的,具有固定的读写端。虽然pipe()系统调用返回了两个文件描述符,但每个进程在使用一个文件描述符之前应该先将另外一个文件描述符关闭。如果需要双向的数据流,则必须通过两次pipe()建立起两个管道。
- 无名管道可以看作是一种特殊的文件,由一组VFS对象(虚拟文件系统)来实现,有对应的磁盘映像,只存在于内存的高速缓存中。Linux在2.6之后的版本中,把管道相关的VFS对象组织成一种特殊文件系统pipefs进行管理,但它在系统目录树中没有安装点,所以用户看不到。
对管道的读写和对普通文件读写差不多,使用通用的read()、write()等,但内核最终会调用管道文件的读写操作函数。
创建无名管道:
pipe()
int pipe(int fileds[2]);
需要包含头文件<unistd.h>
参数:
fileds[2] 是一个输出参数,返回两个文件描述符
0 用于读管道
1 用于写管道
功能:
* 在内缓冲区创建一个管道,主要是建立相关 VFS 对象,
* 并将读写该管道的一对文件描述符保存在filedes[2]中。
* 不再使用管道时,只需关闭两个文件描述符即可。
返回值:
成功返回:0
失败返回:-1,并且在error中存入错误码
从管道中读取数据
进程使用 read() 系统调用从管道中读取数据,内核最终会调用 pipe_read() 函数来实现。
#include <unistd>
ssize_t read(int filedes, void *buf, size_t nbytes);
返回:若成功则返回读到的字节数,若已到文件末尾则返回0,若出错则返回-1
filedes:文件描述符
buf:读取数据缓存区
nbytes:要读取的字节数
Linux2.6.10之前,每个管道仅有一个缓冲区(4KB);而在2.6.11之后,每个管道最多可有16个缓冲区(64KB)。
从管道中读取数据有两种方式:
- 阻塞型读取数据
若 管道大小(管道缓冲区中待读的字节数)为p,用户进程请求读取n个字节,则阻塞型读取情况如表所示:
管道大小 | 至少有一个写进程 | 没有写进程 |
---|---|---|
p=0 | 1. 如果有睡眠写进程,读取n个字节并返回n,当管道缓冲区为空时等待写进程写数据。 2. 如果没有睡眠写进程,等待写进程写数据,然后读取数据 | 返回0 |
0<p<n | 1. 有睡眠写进程同上; 2. 无睡眠写进程,读取p个字节并返回p,管道缓冲区中还剩0个字节 | 读取p个字节并返回p;管道缓冲区中还剩0个字节。 |
p>=n | 读取n个字节,返回n,管道缓冲区中还剩 p-n 个字节 | 读取n个字节,返回n,管道缓冲区中还剩 p-n 个字节 |
简单总结一下,阻塞的情况主要是产生在管道大小p小于n的情况,如果待读的字节数p已经大于等于n也不至于阻塞。
- 非阻塞型读取数据
非阻塞操作通常都是在open()系统调用中指定O_NONBLOCK(非阻塞方式)标志进行请求,但这个方法不适合无名管道,因为它没有open()操作,不过进程可以通过对相应的文件描述符发出 fcntl() 系统调用来请求对管道执行非阻塞操作。在非阻塞情况下,如果管道大小p小于n,则读取p个字节并返回p,读操作完成;否则读取n个字节并返回n,读操作完成。
fcntl
文件控制函数
文件控制函数 fcntl -- file control
头文件:
#include <unistd.h>
#include <fcntl.h>
函数原型:
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
描述:
fcntl()针对(文件)描述符提供控制.参数fd是被参数cmd操作(如下面的描述)的描述符.
针对cmd的值,fcntl能够接受第三个参数(arg)
fcntl函数有5种功能:
1.复制一个现有的描述符(cmd=F_DUPFD).
2.获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).
3.获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).
4.获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).
5.获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW).
F_SETFL
设置给arg描述符状态标志