Linux进程间通信-FIFO命名管道
Linux进程间通信-FIFO命名管道
1、概述
管道因为没有名称,所以只用于进程间的亲缘通信。为了克服这一缺点,提出了命名管道(FIFO),又称命名管道、FIFO文件。
FIFO不同于无名管道,它提供与之关联的路径名,该路径名以FIFO文件的形式存在于文件系统中。这样,即使进程与FIFO的创建进程没有亲属关系,只要能够访问路径,就可以通过FIFO相互通信。不相关的过程也可以通过FIFO交换数据。
FIFO 在文件系统中作为一个特殊的文件而存在。虽然FIFO文件存放在文件系统中,但是FIFO 中的内容却存放在内存中。当使用 FIFO 的进程退出后,FIFO 文件将继续保存在文件系统中以便以后使用。
2、相关函数
一旦创建了一个FIFO,就可用open打开它,一般的文件访问函数(close、read、write等)都可用于FIFO。
FIFO在使用的过程中常用到如下函数
函数 | 释意 |
---|---|
mkfifo | 创建管道 |
open | 打开管道 |
read | 读管道 |
write | 写管道 |
unlink | 关闭管道 |
unlink | 删除管道 |
上述函数中除了mkfifo函数需要注意一下,其他的函数全是标准的IO操作接口,这里不做解释。使用fifo之前需要使用mkfifo创建一个管道,同时会在指定位置创建一个用于描述管道的文件。
#include <sys/types.h>
#include <sys/stat.h>
/*
pathname:FIFO文件名
mode:属性(见文件操作章节)
返回值:若成功则返回0,否则返回-1,错误原因存于errno中。
错误代码:
EACCESS 参数pathname所指定的目录路径无可执行的权限
EEXIST 参数pathname所指定的文件已存在。
ENAMETOOLONG 参数pathname的路径名称太长。
ENOENT 参数pathname包含的目录不存在
ENOSPC 文件系统的剩余空间不足
ENOTDIR 参数pathname路径中的目录存在但却非真正的目录。
EROFS 参数pathname指定的文件存在于只读文件系统内。
*/
int mkfifo(const char * pathname, mode_tmode)
打开管道时,默认是阻塞模式,写管道时如果没有其他进程读管道写管道就会阻塞住。同理,读管道操作也是如此,如果没有进程写管道,读管道就会被阻塞住。打开管道时以O_NONBLOCK的标志打开fifo文件,程序会直接返回,不管fifo的对端是什么状态。
3、例程
写入测试程序,先判断FIFO文件是否存在,如果文件不存在则调用mkfifo创建一个FIFO通道。以OPEN打开FIFO通道,调用fgets获取控制台输入的内容,使用write方法将数据写入到FIFO通道。
#include <errno.h>
#include <stdlib.h> //for exit
#include <stdio.h> //for printf
#include <unistd.h> //for close
#include <sys/stat.h> //for mkfifo
#include <fcntl.h> //for open
#define FIFO_NUM1 "/tmp/fifotest"
#define MAX_BUFFER_SIZE 100
int main(int argc, char * argv[])
{
/* 判断有名管道是否已存在,若尚未创建,则以相应的权限创建 */
if (access(FIFO_NUM1, F_OK) == -1)
{
if ((mkfifo(FIFO_NUM1, 0777) < 0) && (errno != EEXIST))
{
printf("Failed to create fifo file\n");
exit(1);
}
}
/* 以只写阻塞方式打开FIFO管道 */
int fd = open(FIFO_NUM1, O_WRONLY);
if (fd == -1)
{
printf("Failed to open fifo\n");
exit(1);
}
while(1)
{
char buff[MAX_BUFFER_SIZE] = {0};
fgets(buff,sizeof(buff),stdin);
/* 向管道中写入字符串 */
if (write(fd, buff, MAX_BUFFER_SIZE) > 0)
{
printf("Write '%s' to FIFO\n", buff);
}
}
close(fd);
exit(0);
}
读取测试程序,同样先判断FIFO文件是否存在,不存在则调用mkfifo创建一个FIFO通道。使用read循环读取FIFO的内容并打印出来。
#include <errno.h>
#include <stdlib.h> //for exit
#include <stdio.h> //for printf
#include <unistd.h> //for close
#include <sys/stat.h> //for mkfifo
#include <fcntl.h> //for open
#define FIFO_NUM1 "/tmp/fifotest"
#define MAX_BUFFER_SIZE 100
int main(void)
{
/* 判断有名管道是否已存在,若尚未创建,则以相应的权限创建 */
if (access(FIFO_NUM1, F_OK) == -1)
{
if ((mkfifo(FIFO_NUM1, 0777) < 0) && (errno != EEXIST))
{
printf("Failed to create fifo file\n");
exit(1);
}
}
/* 以只读阻塞方式打开有名管道 */
int fd = open(FIFO_NUM1, O_RDONLY);
if (fd == -1)
{
printf("Failed to open fifo\n");
exit(1);
}
while (1)
{
char buff[MAX_BUFFER_SIZE] = {0};
if (read(fd, buff, MAX_BUFFER_SIZE) > 0)
{
printf("Read '%s' from fifo\n", buff);
}
}
close(fd);
exit(0);
}
使用gcc分别编译文件,然后直接调用就可以运行了。可以看到运行结果与预期一致。
$ gcc ./write.c -o write
$ ./write
11
Write '11
' to FIFO
222
Write '222
' to FIFO
333
Write '333
' to FIFO
444
Write '444
' to FIFO
$ gcc ./read.c -o read
$ ./read
Read '11
' from fifo
Read '222
' from fifo
Read '333
' from fifo
Read '444
' from fifo
4、题外话
FIFO管道在创建时,会锁定文件的权限。我上面写的例程,创建FIFO时写入的是0777的权限,实际生成的FIFO文件却是755的权限。也就是说,FIFO文件只有创建者有权限读写,其他成员只能读取,不能执行写入的动作。
$ ls -l /tmp/fifotest
prwxr-xr-x 1 zac zac 0 Nov 18 16:40 /tmp/fifotest
如果有两个应用之间通过FIFO通信,但两个应用属于不同的用户。这时候读取进程按照上面所说的例程方式,先启动创建了FIFO文件。那么等写入进程运行起来后,将无法进行FIFO管道的写入操作。
这时候读取进程需要做出一些小修改。可以每隔一段时间查询一下FIFO文件的状态,等待写入进程成功创建FIFO文件。
xxxxxx
/* 判断有名管道是否已存在,若尚未创建,则等待片刻*/
while(access(FIFO_NUM1, F_OK) == -1)
{
usleep(200*1000);
}
xxxxxx
5、总结
- 要创建和打开管道,只需调用pipe。创建和打开一个FIFO,在调用mkfifo后还需要使用open;
- 管道在所有进程最终关闭后自动消失,只有通过调用unlink才能从文件系统中删除FIFO名称。
- 创建FIFO文件时会锁定文件的写入权限,只有创建者才有资格写入
更多推荐
所有评论(0)