linux c编程:FIFO

摘要:
值得注意的是,FIFO严格遵循先进先出的原则。读取管道和FIFO总是从开头返回数据,而写入管道和FIFO会将数据添加到结尾。管道和FIFOs之间的唯一区别是在人类中重新创建并打开管道。一旦完成这些任务,我/我的管道和FIFO实际上是相同的如果没有进程写入并打开FIFO,设置了阻塞标志的读取操作将被阻塞。当要写入的数据量不大于PIPE_当使用BUF时,Linux将保证写入的原子性。

前面介绍的pipe属于匿名管道

管道的主要局限性正体现在它的特点上:

  • 只支持单向数据流; 
  • 只能用于具有亲缘关系的进程之间; 
  • 没有名字; 
  • 管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配一个页面大小); 
  • 管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式,比如多少字节算作一个消息(或命令、或记录)等等;

如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道,是一种特殊类型的文件。

二,命名管道FIFO

2.1 有名管道相关的关键概念

管 道应用的一个重大限制是它没有名字,因此,只能用于具有亲缘关系的进程间通信,在有名管道(named pipeFIFO)提出后,该限制得到了克服。FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。这样,即 使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信(能够访问该路径的进程以及FIFO的创建进程之 间),因此,通过FIFO不相关的进程也能交换数据。值得注意的是,FIFO严格遵循先进先出(first in first out),对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。它们不支持诸如lseek()等文件定位操作。

2.2有名管道的创建

命名管道可以从命令行上创建,命令行方法是使用下面这个命令: 

$ mkfifo filename 

命名管道也可以从程序里创建,相关函数有:

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

该函数的第一个参数是一个普通的路径名,也就是创建 后FIFO的名字。第二个参数与打开普通文件的open()函数中的mode 参数相同。 如果mkfifo的第一个参数是一个已经存在的路径名时,会返回EEXIST错误,所以一般典型的调用代码首先会检查是否返回该错误,如果确实返回该错 误,那么只要调用打开FIFO的函数就可以了。一般文件的I/O函数都可以用于FIFO,如closereadwrite等等。

2.3有名管道的打开规则(与匿名管道一样)

FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一量这些工作完成之后,它们具有相同的语义。

man帮助说明:The only difference between pipes and FIFOs is the manner in which they are created and opened. Once these tasks have been accomplished, I/O on pipes and FIFOs has exactly the same semantics

有名管道比管道多了一个打开操作:open

FIFO的打开规则:

如果当前打开操作是为读而打开FIFO时,若已经有相应进程为写而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为写而打开该FIFO(当前打开操作设置了阻塞标志);或者,成功返回(当前打开操作没有设置阻塞标志)。

如果当前打开操作是为写而打开FIFO时,如果已经有相应进程为读而打开该FIFO,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为读而打开该FIFO(当前打开操作设置了阻塞标志);或者,返回ENXIO错误(当前打开操作没有设置阻塞标志)。

2.4有名管道的读写规则

FIFO中读取数据:

约定:如果一个进程为了从FIFO中读取数据而阻塞打开FIFO,那么称该进程内的读操作为设置了阻塞标志的读操作。

  • 如果有进程写打开FIFO,且当前FIFO内没有数据,则对于设置了阻塞标志的读操作来说,将一直阻塞。对于没有设置阻塞标志读操作来说则返回-1,当前errno值为EAGAIN,提醒以后再试。 
  • 对于设置了阻塞标志的读操作说,造成阻塞的原因有两种:当前FIFO内有数据,但有其它进程在读这些数据;另外就是FIFO内没有数据。解阻塞的原因则是FIFO中有新的数据写入,不论信写入数据量的大小,也不论读操作请求多少数据量。 
  • 读打开的阻塞标志只对本进程第一个读操作施加作用,如果本进程内有多个读操作序列,则在第一个读操作被唤醒并完成读操作后,其它将要执行的读操作将不再阻塞,即使在执行读操作时,FIFO中没有数据也一样(此时,读操作返回0)。 
  • 如果没有进程写打开FIFO,则设置了阻塞标志的读操作会阻塞。

注:如果FIFO中有数据,则设置了阻塞标志的读操作不会因为FIFO中的字节数小于请求读的字节数而阻塞,此时,读操作会返回FIFO中现有的数据量。

FIFO中写入数据:

约定:如果一个进程为了向FIFO中写入数据而阻塞打开FIFO,那么称该进程内的写操作为设置了阻塞标志的写操作。

对于设置了阻塞标志的写操作:

  • 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果此时管道空闲缓冲区不足以容纳要写入的字节数,则进入睡眠,直到当缓冲区中能够容纳要写入的字节数时,才开始进行一次性写操作。 
  • 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。FIFO缓冲区一有空闲区域,写进程就会试图向管道写入数据,写操作在写完所有请求写的数据后返回。

对于没有设置阻塞标志的写操作:

  • 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。在写满所有FIFO空闲缓冲区后,写操作返回。 
  • 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果当前FIFO空闲缓冲区能够容纳请求写入的字节数,写完后成功返回;如果当前FIFO空闲缓冲区不能够容纳请求写入的字节数,则返回EAGAIN错误,提醒以后再写;

来看一个具体的实现:

write的函数:

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <sys/stat.h>

#include <sys/types.h>

#include <fcntl.h>

 

int main(int argc, char **argv)

{

    int infd;

    int fd;

    char buf[1024*4];

    int n = 0;

    char *path="/home/zhf/c_prj/test.c";

    char *path1="/home/zhf/c_prj/tmpfifo";

    infd = open(path,O_RDONLY);

    if(infd == -1){

        perror("open error");

        exit(EXIT_FAILURE);

    }

 

    if(mkfifo(path1,0644) == -1){

        perror("mkfifo error");

        exit(EXIT_FAILURE);

    }

    fd = open(path1,O_WRONLY);

    if(fd == -1){

        perror("open fifo error");

        exit(EXIT_FAILURE);

    }

    while((n = read(infd,buf,1024*4))){

        write(fd,buf,n);

    }

    close(infd);

    close(fd);

    printf("write success ");

    return 0;

}

read的函数:

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <sys/stat.h>

#include <sys/types.h>

#include <fcntl.h>

 

int main(int argc, char **argv)

{

    int outfd;

    int fd;

    char buf[1024*4];

    int n = 0;

    char *path="/home/zhf/c_prj/tmp.txt";

    char *path1="/home/zhf/c_prj/tmpfifo";

    outfd = open(path,O_WRONLY | O_CREAT | O_TRUNC);

    if(outfd == -1){

        perror("open error");

        exit(EXIT_FAILURE);

    }

    fd = open(path1,O_RDONLY);

    if(fd == -1){

        perror("open fifo error");

        exit(EXIT_FAILURE);

    }

    while((n = read(fd,buf,1024*4))){

        write(outfd,buf,n);

    }

    close(outfd);

    close(fd);

    printf("read success ");

    return 0;

}

运行步骤:

首先在write函数中打开/home/zhf/c_prj/test.c文件并且建立tmpfifo文件。以写的方式打开FIFO文件

test.c中读取数据存入buf数组中,并继续写入tmpfifo文件中

read函数中打开/home/zhf/c_prj/tmp.txt以及tmpfifo文件,以读的方式打开FIFO文件。并将FIFO文件的数据写入tmp.txt

运行结果

linux c编程:FIFO第1张

免责声明:文章转载自《linux c编程:FIFO》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇linux应用之bugfree的安装及配置ES6学习--搭建环境下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Unity碰撞消息(OnCollisionXXXX)和触发消息(OnTriggerXXXX)的调用情境

MonoBehaviour中的消息非常多,一共有62个! 除了必须关注的脚本生命周期的一系列函数外,还有其他两组比较常混淆的消息:碰撞和触发。 按3D和2D物体区分,又分为碰撞:Collision、Collision2D。触发:Trigger、Trigger2D。 以3D物体为例做了如下实验,分别测试下OnColiisionXXXX碰撞消息和OnTrigg...

Laravel框架安装RabbitMQ消息中间件步骤

Laravel5.6 整合 RabbitMQ 消息队列   简介: Laravel 队列为不同的后台队列服务提供了统一的 API,例如 Beanstalk,Amazon SQS,Redis,甚至其他基于关系型数据库的队列。队列的目的是将耗时的任务延时处理,比如发送邮件,从而大幅度缩短 Web 请求和响应的时间。 队列配置文件存放在 config/queue...

openssl3.0 加密算法库编程精要 04 详解 EVP API 消息摘要

4.1 消息摘要的概念   消息摘要有好几个名字,比如单项散列函数,Hash 函数,它是一个将可变长度的输入串转换为一个固定长度的输出 串的函数。大多数消息摘要算法都是公开的,它的安全性依赖于它的单向性,如果仅获取到消息摘要的结果,想要从结果 反推出原文几乎是不可能的事情。并且对于输入串的细微改变,都会引发输出串的雪崩式变化,所以消息摘要一般用于校 验数据...

小程序:前端防止用户重复提交&amp;amp;即时消息(IM)重复发送问题解决

背景: 最近参与开发的小程序,涉及到即时消息(IM)发送的功能; 聊天界面如下,通过键盘上的【发送】按钮,触发消息发送功能 问题发现: 功能开发完毕,进入测试流程;测试工程师反馈说: 在Android手机上,在极短的时间内频繁点击键盘上的【发送】按钮,消息会重复发送;IOS上该问题不太明显 本以为是普通的防重复提交问题,于是自然想到通过设定flag/js...

网络协议和管理

网络的特征 速度 成本 安全性 可用性 可扩展性 可靠性 拓扑 物理拓扑分类 OSI七层模型(OSI System Interconnection) 物理层:二进制传输 数据链路层:访问介质 网络层:数据传输 传输层:端到端连接 会话层:主机间通信 表示层:数据表示 应用层:网络进程访问应用层...

kill命令

$ kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SI...