IPC - 命名管道(fifo)- 使用

Posted Redamanc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IPC - 命名管道(fifo)- 使用相关的知识,希望对你有一定的参考价值。

命名管道

我们前面介绍了匿名管道pipe),匿名管道实际上就是:内存上的一块缓存
它的主要实现方式是借助于fork之后父进程子进程会共享之前已经打开的文件描述符,并且父进程关闭fd[0]读端,子进程关闭fd[1]写端来实现的。

之所以说它是匿名管道,是因为,它在内核中开辟的缓存并没有一个名字。
那么,通过名字就能理解了,命名管道就是在内核中开辟的缓存有自己的名字了。

其实,这也就是命名管道和匿名管道之间的区别。
命名管道的本质和匿名管道是一样的,都是一块内存上的缓存
但是不一样的是,命名管道在开辟缓存的同时,还会与之对应一个文件系统中的管道文件
之后,我们通过对文件的处理(openreadwrite)等,就可以操作这一块缓存了。

函数原型

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char* pathname, mode_t mode);

函数返回值:
成功 返回:0
失败 返回:-1

原型解读:
参数pathname:指定想要创建的FIFO文件路径
参数mode:指定创建的FIFO文件访问模式(权限)。

注意事项

  • 只要对FIFO有适当的访问权限,FIFO可以用在两个没有任何关系的进程之间通信。(对比pipe:只能使用在血缘关系的进程中:父子进程、兄弟进程);
  • 本质上是内核中的一块缓存,另在文件系统中以一个特殊的设备文件(管道文件)存在;
  • 在文件系统中只有一个索引块存放文件的路径,没有数据块,所有的数据都存放在内核中;
  • 命名管道必须读和写同时打开,否则单独地读和写会造成堵塞
  • 可以使用命令mkfifo创建命名管道(内部其实还是调用了mkfifo()函数);
  • 对FIFO的操作和普通文件一样
  • 一旦已经用mkfifo创建了一个FIFO文件,就可以用open打开它,一般的文件I/O函数closereadwrite)等都可以操作。

FIFO出错信息

  • EACCES(无存取权限)
  • EEXIST(指定文件不存在)
  • ENAMETOOLONG(路径名太长)
  • ENOENT(包含的目录不存在)
  • ENOSPC(文件系统剩余空间不足)
  • ENOTDIR(文件路径无效)
  • EROFS(指定的文件存在于只读文件系统中)

简单应用

为了模拟两个没有任何关系的进程,我们写两个文件:
fifo_read.cfifo_write.c
首先是从命名管道中读取数据的fifo_read.c

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <memory.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    if (argc < 2)
    {
        printf("usage:%s fifo\\n", argv[0]);
        exit(1);
    }
    printf("open fifo read...\\n");
    // 打开命名管道
    int fd = open(argv[1], O_RDONLY);
    if (fd < 0)
    {
        perror("open error");
        exit(1);
    }
    else
    {
        printf("open fifo read success: %d\\n", fd);
    }
    // 从命名管道中读取数据
    char buf[512];
    memset(buf, 0, sizeof(buf));
    while (read(fd, buf, sizeof(buf)) < 0)
    {
        perror("read error");
    }
    printf("%s\\n", buf);

    close(fd);

    exit(0);
}

接下来是向命名管道中写入数据fifo_write.c

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
    if (argc < 2)
    {
        printf("usage:%s fifo\\n", argv[0]);
        exit(1);
    }

    printf("open fifo write...\\n");
    // 打开命名管道
    int fd = open(argv[1], O_WRONLY);
    if (fd < 0)
    {
        perror("open error");
        exit(1);
    }
    else
    {
        printf("open fifo write success:%d\\n", fd);
    }
    // 写数据
    char *s = "1234567890";
    size_t size = strlen(s);
    if(write(fd,s,size) != size)
    {
        perror("write error");
    }
    
    close(fd);

    exit(0);
}

接下来我们通过命令行mkfifo来创建FIFO文件:
在这里插入图片描述
我们可以通过命令ls -l来查看详细的文件信息
在这里插入图片描述
可以看到该文件是一个管道文件
接下来我们通过执行我们写的文件,需要传入管道文件的参数:
在这里插入图片描述
同样的,我们调用fifo_write写文件:
在这里插入图片描述
这也刚好印证了我们前面说过的特性:
命名管道必须读和写同时打开,否则单独地读和写会造成堵塞
所以我们同时运行

在这里插入图片描述
可以看到打开的文件描述符都是3,并且,可以正常的读出管道中的数据。

参考资料

【1】LuckY_chh. bilibili. Linux系统程序设计–IPC. 2018-11-27

以上是关于IPC - 命名管道(fifo)- 使用的主要内容,如果未能解决你的问题,请参考以下文章

IPC - 命名管道(fifo)- 使用

IPC - 命名管道(fifo)- 使用

LINUX FIFO 命名管道 (IPC) 在特定文件描述符处停止写入/读取消息

什么时候应该使用 fifo 文件/命名管道?

进程间通信——命名管道

操作系统:进程通信(IPC)