【Linux】进程间通信-命名管道FIFO

2023-11-07

命名管道概述

如果我们要在不相关的进程间交换数据,那么使用FIFO文件将会十分方便。

FIFO文件通常也称为命名管道(named pipe)。命名管道是一种特殊类型的文件,它在文件系统中以文件名的形式存在。


创建命名管道

创建命名管道一般有两种方式:

命令行方式

一个比较旧的方式是:

mknod filename p

这个命令并未出现在X/Open规范的命令列表中,所以可能并不是所有的类Unix系统都可以这样做。

推荐的做法是:

mkfifo filename

函数调用方式

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
int mknod(const char *pathname, mode_t mode | S_FIFO, (dev_t)0);
函数说明
    mkfifo()会依参数pathname建立特殊的FIFO文件,该文件必须不存在,而参数mode为该文件的权限(mode%~umask),因此 umask值也会影响到FIFO文件的权限。Mkfifo()建立的FIFO文件其他进程都可以用读写一般文件的方式存取。当使用open()来打开 FIFO文件时,O_NONBLOCK旗标会有影响
    1、当使用O_NONBLOCK 旗标时,打开FIFO 文件来读取的操作会立刻返回,但是若还没有其他进程打开FIFO 文件来读取,则写入的操作会返回ENXIO 错误代码。
    2、没有使用O_NONBLOCK 旗标时,打开FIFO 来读取的操作会等到其他进程打开FIFO文件来写入才正常返回。同样地,打开FIFO文件来写入的操作会等到其他进程打开FIFO 文件来读取后才正常返回。
返回值
    若成功则返回0,否则返回-1,错误原因存于errno中。
错误代码
    EACCESS 参数pathname所指定的目录路径无可执行的权限
    EEXIST 参数pathname所指定的文件已存在。
    ENAMETOOLONG 参数pathname的路径名称太长。
    ENOENT 参数pathname包含的目录不存在
    ENOSPC 文件系统的剩余空间不足
    ENOTDIR 参数pathname路径中的目录存在但却非真正的目录。
    EROFS 参数pathname指定的文件存在于只读文件系统内。


代码演示

下面代码演示了mkfifo函数的用法:

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

int main()
{
    int res = mkfifo("~/Test/PipeTest/my_fifo",0777);
    if(!res)
	printf("FIFO created\n");

    exit(EXIT_SUCCESS);
}
运行结果:



linux中ls命令的-F选项是列出文件的类型。


访问FIFO文件

同样有两种方式访问FIFO文件。

命令行方式

首先用cat命令读取刚才创建的FIFO文件:

cat < /tmp/my_fifo

这个时候,cat命令将一直挂起,直到终端或者有数据发送到FIFO中。


然后尝试向FIFO中写数据(在另外一个终端执行这个命令)

echo "FIFO test" > /tmp/my_fifo

这个时候cat将会输出内容。


函数调用方式

首先需要注意的是:

与通过pipe调用创建管道不同,FIFO是以命名文件的形式存在,而不是打开的文件描述符,所以在对它进行读写操作之前必须先打开它。

1、使用open函数打开FIFO文件

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);

打开FIFO文件和普通文件的区别有2点:

第一个是不能以O_RDWR模式打开FIFO文件进行读写操作。这样做的行为是未定义的。

因为我们通常使用FIFO只是为了单向传递数据,所以没有必要使用这个模式。

如果确实需要在程序之间双向传递数据,最好使用一对FIFO或管道,一个方向使用一个。或者采用先关闭在重新打开FIFO的方法来明确改变数据流的方向。

第二是对标志位的O_NONBLOCK选项的用法。

使用这个选项不仅改变open调用的处理方式,还会改变对这次open调用返回的文件描述符进行的读写请求的处理方式。

O_RDONLY、O_WRONLY和O_NONBLOCK标志共有四种合法的组合方式:

  • flags=O_RDONLY:open将会调用阻塞,除非有另外一个进程以写的方式打开同一个FIFO,否则一直等待。
  • flags=O_WRONLY:open将会调用阻塞,除非有另外一个进程以读的方式打开同一个FIFO,否则一直等待。
  • flags=O_RDONLY|O_NONBLOCK:如果此时没有其他进程以写的方式打开FIFO,此时open也会成功返回,此时FIFO被读打开,而不会返回错误。
  • flags=O_WRONLY|O_NONBLOCK:立即返回,如果此时没有其他进程以读的方式打开,open会失败打开,此时FIFO没有被打开,返回-1。


测试代码:

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

#define FIFO_NAME "/tmp/my_fifo"

int main(int argc,char *argv[])
{
        int res,i;
        int open_mode=0;
        if(argc < 2){
                fprintf(stderr,"Usage:%s<some combination of \
                        O_RDONLY,O_WRONLY,O_NONBLOCK\n",*argv);
                exit(EXIT_FAILURE);
        }
        argv++;
        if(strncmp(*argv,"O_RDONLY",8)==0)open_mode|=O_RDONLY;
        if(strncmp(*argv,"O_WRONLY",8)==0)open_mode|=O_WRONLY;
        if(strncmp(*argv,"O_NONBLOCK",10)==0)open_mode|=O_NONBLOCK;
       
	for(i = 1;i < argc;++i)
	{
       		argv++;
        	if(*argv){
                	if(strncmp(*argv,"O_RDONLY",8)==0)open_mode|=O_RDONLY;
                	if(strncmp(*argv,"O_WRONLY",8)==0)open_mode|=O_WRONLY;
                	if(strncmp(*argv,"O_NONBLOCK",10)==0)open_mode|=O_NONBLOCK;
        	}
	}
        if(access(FIFO_NAME,F_OK)==-1){
                res=mkfifo(FIFO_NAME,0777);
                if(res!=0){
                        fprintf(stderr,"Could not create fifo %s\n",FIFO_NAME);
                        exit(EXIT_FAILURE);
                }
        }
        printf("process %d open FIFO with %d\n",getpid(),open_mode);
        res=open(FIFO_NAME,open_mode);
        printf("process %d result %d\n",getpid(),res);
        sleep(5);
        if(res!=-1)close(res);
        printf("process %d finished\n",getpid());
        exit(EXIT_SUCCESS);
}

运行结果:


对FIFO文件进行读写操作

open函数调用中的参数标志O_NONBLOCK会影响FIFO的读写操作。

规则如下:

  • 对一个空的阻塞的FIFO的read调用将等待,直到有数据可以读的时候才继续执行/
  • 对一个空的非阻塞的FIFO的read调用立即返回0字节。
  • 对一个完全阻塞的FIFO的write调用将等待,直到数据可以被写入时才开始执行。
    • 系统规定:如果写入的数据长度小于等于PIPE_BUF字节,那么或者写入全部字节,要么一个字节都不写入。
注意这个限制的作用:
当只使用一个FIF并允许多个不同的程序向一个FIFO读进程发送请求的时候,为了保证来自不同程序的数据块 不相互交错,即每个操作都原子化,这个限制就很重要了。如果能够包子所有的写请求是发往一个阻塞的FIFO的,并且每个写请求的数据长父小于等于PIPE_BUF字节,系统就可以确保数据绝不会交错在一起。通常将每次通过FIFO传递的数据长度限制为PIPE_BUF是一个好办法。
  • 在非阻塞的write调用情况下,如果FIFO 不能接收所有写入的数据,将按照下面的规则进行:
    • 请求写入的数据的长度小于PIPE_BUF字节,调用失败,数据不能被写入。
    • 请求写入的数据的长度大于PIPE_BUF字节,将写入部分数据,返回实际写入的字节数,返回值也可能是0。

其中。PIPE_BUF是FIFO的长度,它在头文件limits.h中被定义。在linux或其他类UNIX系统中,它的值通常是4096字节。


在这里需要注意的是:

有两个进程去访问FIFO管道时,Linux会安排好两个进程之间的调度,使得两个进程在可以运行的时候运行,在不能运行的时候阻塞。


程序实例

下面的程序用命名管道在两个独立的进程之间通信,模拟了消费者和生产者程序。

生产者程序fork3.c:

//生产者程序
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>

#define FIFO_NAME "/tmp/my_fifo"
#define BUFFER_SIZE PIPE_BUF
#define TEN_MSG (1024 * 1024 * 10)

int main()
{
    int pipe_fd;
    int res;
    int open_mode = O_WRONLY;
    int bytes_sent = 0;
    char buffer[BUFFER_SIZE + 1];

    printf("Productor Program beginning...\n");

    //检查FIFO文件是否存在
    if(access(FIFO_NAME, F_OK) == -1){
        res = mkfifo(FIFO_NAME, 0777);
        if(res != 0){
            fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME);
            exit(EXIT_FAILURE);
        }
    }
    printf("Process %d opening FIFO O_WRONLY\n", getpid());
    //打开FIFO文件
    pipe_fd = open(FIFO_NAME, open_mode);
    printf("Process %d result %d\n", getpid(), pipe_fd);

    if(pipe_fd != -1){
        while(bytes_sent < TEN_MSG){
            res = write(pipe_fd, buffer, BUFFER_SIZE); //向FIFO写入数据
            if(res == -1){
                fprintf(stderr, "Write error on pipe\n");
                exit(EXIT_FAILURE);
            }
            bytes_sent += res;
        }
        (void)close(pipe_fd);
    }
    else{
        exit(EXIT_FAILURE);
    }
    
    printf("Process %d finished\n", getpid());
    exit(EXIT_SUCCESS);
}

消费者程序fork4.c:

//消费者程序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>

#define FIFO_NAME "/tmp/my_fifo"
#define BUFFER_SIZE PIPE_BUF

int main()
{
    int pipe_fd;
    int res;

    int open_mode = O_RDONLY;
    char buffer[BUFFER_SIZE + 1];
    int bytes = 0;

    printf("COnsumer Program beginning...");
    memset(buffer,'\0', sizeof(buffer));

    printf("Process %d opeining FIFO O_RDONLY\n", getpid());
    pipe_fd = open(FIFO_NAME, open_mode);
    printf("Process %d result %d\n", getpid(), pipe_fd);

    if (pipe_fd != -1)
    {
        do{
            res = read(pipe_fd, buffer, BUFFER_SIZE);
            bytes += res;
        }while(res > 0);
        close(pipe_fd);
    }
    else
    {
        exit(EXIT_FAILURE);
    }

    printf("Process %d finished, %d bytes read\n", getpid(), bytes);
    exit(EXIT_SUCCESS);
}

运行结果:






可以发现读进程只运行了不到0.1S的时间,却读取了10MB的数据。这说明管道在程序之间传递数据是很有效率的。

删除FIFO文件

FIFO文件使用完毕之后需删除,以免造成垃圾文件。

#include <unistd.h>
int unlink(const char *pathname);

关于unlink的详细内容参考:

unlink(2) - Linux man page


如需转载,请注明出处:http://blog.csdn.net/xiajun07061225/article/details/8471777


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Linux】进程间通信-命名管道FIFO 的相关文章

随机推荐

  • 解密Kerberos流量

    以下主要演示如何通过将keytab导入到wireshark 实现对Kerberos协议进行解密 Keytab 那么Keytab是什么 keytab是可以理解为一个密钥表 是key table的缩写 用途类似于用户的密码 keytab中包含一
  • Docker实战:docker compose 搭建Rocketmq

    1 配置文件准备 1 1 新建目录 home docker data rocketmq conf mkdir home docker data rocketmq conf 1 2 在上面目录下新建文件broker conf文件 内容如下 b
  • JSX的基础使用(二)

    1 事件绑定 React 事件的 命名采用小驼峰式 camelCase 而不是纯小写 们需要 通过 传入一个事件处理函数 这个函数会在事件发生时被执行 2 this绑定 bind给btnClick显示绑定this 使用 ES6 class
  • 面向过程编程

    p 面向过程编程 p p p
  • Qt 使用中文引起的错误error: C2001: 常量中有换行符;error: C1071: 在注释中遇到意外的文件结束

    中文编码问题导致的错误 解决办法 1 使用英文注释 2 在中文的结尾加上英文 或者空格 3 修改设置 编辑 gt 选择编码 gt system gt 编码保存
  • 六、Audio-ALSA架构中的codec

    一 codec简介 处理器如果既想 听到 外界的声音 又想向外界传达自己的 心声 那么就需要同时用到 DAC 和 ADC 这两款芯片 那是不是买两颗 DAC 和 ADC 芯片就行了呢 答案肯定是可以的 但是音频不单单是能出声 能听到就行 我
  • VC6 ”add files to project“功能失效的解决方案

    在Win7系统下 安装MS2007以后 VC6 0 add files to project 功能失效了 网上的资料表明 这是与MS2007不兼容导致的 其具体解决方案如下 1 到如下网址 http download microsoft c
  • 服务器硬件规格常用查看命令——磁盘相关命令

    smartctl smartctl是一个能够控制和监控磁盘的SMART Self Monitoring Analysis and Reporting Technology 自我监测 分析和报告技术 命令 使用方法 提示 使用该命令只能查看到
  • Apache Hop-使用介绍【持续完善中】

    文章目录 Pipelines 管道 Pipeline Editor 管道编辑器 TOOLBAR Create a Pipeline 创建管道 How pipelines work 管道如何工作 Concepts 概念 Create a pi
  • 计算机组成原理(七)——总线BUS

    总线 BUS 总线 BUS 概念 连接计算机系统各个功能部件的信息传输线 是各个部件共享数据及信息的传输介质 用来连接计算机系统各功能部件而构成一个完整系统 实际上是一组信号线 广义地讲 任何连接两个以上电子元器件的导线都可以称为总线 计算
  • 微服务系列文章之 Redisson实现分布式锁

    一 高效分布式锁 当我们在设计分布式锁的时候 我们应该考虑分布式锁至少要满足的一些条件 同时考虑如何高效的设计分布式锁 这里我认为以下几点是必须要考虑的 1 互斥 在分布式高并发的条件下 我们最需要保证 同一时刻只能有一个线程获得锁 这是最
  • 【go】异步任务解决方案Asynq实战

    文章目录 一 Asynq介绍 二 所需工具 三 代码示例 四 Reference 一 Asynq介绍 Asynq 是一个 Go 库 一个高效的分布式任务队列 Asynq 工作原理 客户端 生产者 将任务放入队列 服务器 消费者 从队列中拉出
  • openEuler之RPM软件包管理命令

    RPM命令介绍 安装软件 rpm i xx rpm 卸载软件 rpm e xx rpm 升级形式安装 rpm U xx rpm 常用参数 v 显示详细信息 h 显示文本进度条 1 安装软件 1 下载RPM包 root localhost l
  • K8s企业版多节部署

    K8s企业版多节部署 实验步骤 K8s的单节点部署 master2节点部署 负载均衡部署 使用双机热备 k8s网站页面 实验环境 使用Nginx做负载均衡 lb01 192 168 217 136 24 CentOS 7 5 lb02 19
  • DLL+资源模块切换

    MFC程序中存在一个模块状态 Module State 的问题 也就是资源重复的问题 此处的术语模块是指一个可执行程序 或指其操作不依赖于应用程序的其余部分但使用MFC运行库的共享副本的一个DLL 或一组DLL 我们所创建的MFC DLL就
  • VS2019配置opencv详细图文教程和测试代码

    摘要 vs2019新鲜出炉 配置opencv又有哪些不一样呢 这个教程将会一步一步的教你如何配置opencv和跑动opencv一个简单的项目 测试代码请在原文找到 转发备注原文链接 https xygeng cn post 151 html
  • 机器学习数据获取与处理

    数据获取与处理 以CV任务为主 课程目的 数据的获取途径 数据处理与标注 数据预处理方法 模型训练评估 一 数据集的获取 通常 我们的数据来源于各个比赛平台 首先是AIStudio中的数据集 大部分经典数据集例如百度AI Studio Ka
  • 小程序用户隐私保护协议纯文案修改指引

    目录 一 修改缘由 二 官方指引 三 填写入口 一 修改缘由 小程序提交审核不通过 审核失败原因 存在平台未允许的服务内容 违反 微信小程序平台运营规范常见拒绝情形3 4 详情描述 你好 你的小程序涉及收集 使用和存储用户信息 请增加 用户
  • C++模板的特化(specialization)和偏特化(partial specialization)

    C 模板的特化及偏特化 类模板全特化 对类中的某个成员函数进行特化处理 类模板的偏特化 个数偏特化 范围偏特化 函数模板全特化 函数模板偏特化 模板函数和模板类有的时候可能需要对传入的不同类型进行不同的处理 比如说有的模板传入int或dou
  • 【Linux】进程间通信-命名管道FIFO

    命名管道概述 如果我们要在不相关的进程间交换数据 那么使用FIFO文件将会十分方便 FIFO文件通常也称为命名管道 named pipe 命名管道是一种特殊类型的文件 它在文件系统中以文件名的形式存在 创建命名管道 创建命名管道一般有两种方