匿名管道只允许有亲缘关系的进程之间进行通信,但是命名管道可以允许没有亲缘关系的进程进行通信。
FIFO文件(命名管道)是一种特殊类型的文件,在linux中用于进程间通信。FIFO文件允许不相关的进程通过读取和写入相同的文件进行通信。
FIFO文件位于文件系统中,可以像其他文件一样进行访问和管理。FIFO文件可以通过名称进行标识和引用。
进程通过调用mkfifo函数创建一个FIFO文件,FIFO文件对应一个fifo inode, fifo inode和pipe inode底层实现相同。命名管道使用文件名创建管道,所以是命名管道。
命名管道通过mkfifo创建完后,进程调用open打开FIFO文件,由于每个文件都对应唯一的inode节点。所以多个进程都是打开相同的inode节点,成功打开FIFO文件后,进程能正常读写管道完成进程间通信。打开FIFO文件需要知道FIFO文件路径,进程间只要知道FIFO文件路径,就能建立连接,完成通信。进程之间都能看到管道内存空间,所以进程间能正常通信。
调用一次mkfifo函数只能产生一个管道(一个缓冲区),所以命名管道是半双工模式。命名管道也需要创建多个命名管道实现全双工通信
FIFO write操作是原子的
(1)小于或等于PIPE_BUF的写操作:如果写入的数据量小于或等于PIPE_BUF的大小,那么写操作将被视为原子的。这意味着数据将作为一个整体写入,不会被分割。
(2)大于PIPE_BUF的写操作:如果写入的数据量超过了PIPE_BUF的大小,那么写操作将可能被分割,也就是说,写操作可能不是原子的。
(3)写操作的阻塞特性:FIFO的写操作是阻塞的(如果没有设置为非阻塞模式),这意味着如果没有读进程在另一端等待接收数据,写进程将被阻塞,直到有读进程出现。
可以把文件描述符设置为非阻塞模式对FIFO进行读写,
(1)当使用 open 函数打开一个文件描述符时,可以传递 O_NONBLOCK 标志。这将使任何后续对该文件描述符的读或写操作变为非阻塞的。
int writefd = open(FIFO1, O_WRONLY | O_NONBLOCK, 0);
这里的 FIFO1 是FIFO的名字,O_WRONLY 表示只写模式,O_NONBLOCK 表示非阻塞模式。
(2)使用 fcntl 函数:如果一个文件描述符已经打开,或者在创建时无法指定 O_NONBLOCK 标志(如通过 pipe 调用创建的管道),可以使用 fcntl 函数来修改文件描述符的属性,使其变成非阻塞模式。示例代码如下:
int flags;
if ((flags = fcntl(fd, F_GETFL, 0)) < 0) {
err_sys("F_GETFL error");
}
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) < 0) {
err_sys("F_SETFL error");
}
fd 是要修改的文件描述符。设置非阻塞模式后,读或写操作将立即返回,无论操作是否完成。如果操作不能立即完成,函数将返回一个错误,使得程序可以更好地处理并发和异步事件。
FIFO与NFS的关系
FIFO是基于本地文件系统的,它们只能在同一台物理机器上的进程之间进行通信。这是因为它们依赖于本地文件系统来创建和维护命名管道的存在。网络文件系统(NFS)允许远程主机访问本地文件系统上的文件,就好像它们是本地的一样。但是,NFS并不支持FIFO,因为NFS的设计并未包含对命名管道的支持。尝试在NFS挂载的文件系统上创建或使用FIFO可能会失败,因为远程服务器上的文件系统可能无法正确处理或识别FIFO类型。
管道和FIFO的限制
(1)OPEN_MAX 定义了一个进程可以同时打开的最大文件描述符数量。文件描述符是操作系统用于标识进程所打开的文件、套接字、管道等输入/输出资源的整数。每个进程都有一个文件描述符表,用来跟踪这些资源的状态。为了防止资源耗尽或滥用,操作系统对这个数量进行了限制。
(大概在几千)
(2)PIPE_BUF 定义了可以原子地写入管道或FIFO(命名管道)的最大数据量。"原子地"意味着如果一个写操作小于等于 PIPE_BUF 大小,那么该写操作不会被分割成多个较小的操作,从而保证了数据的完整性和一致性。
(大概在几千字节通常定义为4096字节)
使用FIFO实现的一个迭代服务器。
在服务器端使用的公共的fifo命名管道(路径名被所有客户端知道),每个客户端创建自己的fifo文件,然后以只写的方式打开服务器的fifo文件,向fifo写自己的fifo路径名和要打开的文件名。
服务器端以只读方式打开自己的fifo文件,然后读取客户端发送的fifo管道,然后以只写的方式打开,向管道发送内容。
客户端以只读的方式打开自己的fifo文件读取服务器端发送的内容。