Skip to content

命名管道-FIFO

FIFO介绍

FIFO常被称为命名管道,以和匿名管道pipe进行区分。匿名管道pipe只能用于“有血缘关系”的进程间通信。但通过命名管道FIFO不相关的进程也能交换数据。

FIFO是Linux基础文件类型中的一种(文件类型为p,可通过 ls -l 查看文件类型)。但FIFO文件在磁盘上没有数据块,文件大小为0,仅仅用来标识内核中一条通道。进程可以打开这个文件进行read/write,实际上是在读写内核缓冲区,这样就实现了进程间通信。

创建管道

  • 方式1 使用命令 mkfifo

    命令格式:mkfifo 管道名,例如:mkfifo myfifo

  • 方式2 使用函数

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

    参数说明和返回值可以查看man 3 mkfifo

当创建了一个FIFO,就可以使用open函数打开它,常见的文件I/O函数都可用于FIFO。如:close、read、write、unlink等。

FIFO严格遵循先进先出(first in first out),对FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。它们不支持诸如lseek()等文件定位操作。

使用FIFO完成两个进程通信

  • 使用FIFO完成两个进程通信的示意图

思路

进程A

  • 创建一个fifo文件:myfifo
  • 调用open函数打开myfifo文件
  • 调用write函数写入一个字符串如:“hello world”(其实是将数据写入到了内核缓冲区)
  • 调用close函数关闭myfifo文件

进程B

  • 调用open函数打开myfifo文件
  • 调用read函数读取文件内容(其实就是从内核中读取数据)
  • 打印显示读取的内容
  • 调用close函数关闭myfifo文件

注意

myfifo文件是在进程A中创建的,如果先启动进程B会报错。思考一下如何解决这个问题呢???

根据 open 函数的特性,打开失败会返回-1,给进程 B 添加异常处理来处理,当 open 的返回值不为 -1 的时间再继续运行程序就可以了。

代码实现

processA.c

c
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int ret = mkfifo("fifo", 0744);

    ret = open("fifo", O_WRONLY);

    write(ret, "hello b", 8);
    close(ret);

    return 0;
}

processB.c

c
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int fd;
    while (1)
    {
        fd = open("fifo", O_RDONLY);
        if (fd != -1)
            break;
    }

    char buf[1024] = {0};
    read(fd, buf, sizeof(buf));
    puts(buf);
    close(fd);

    // execlp("rm", "rm", "fifo", NULL);
    unlink("fifo");

    return 0;
}

稍微有点的相关函数

cpp
int mkfifo(const char *pathname, mode_t mode);  // 创建有名管道
int unlink(const char *pathname);   // 删除文件
int rename(const char *oldpath, const char *newpath);   // 类似于linux系统命令mv
int link(const char *oldpath, const char *newpath); // 建立硬链接

管道的缺陷

任意通信的两方都要建立管道,如果需要通信的进程有很多那么就需要建立很多管道。