命名管道概述

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

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

创建命名管道

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

命令行方式

一个比较旧的方式是:

mknod filename p

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

推荐的做法是:

mkfifo filename

函数调用方式

[cpp] view plain copy  print?
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. int mkfifo(const char *pathname, mode_t mode);
  4. 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函数的用法:

[cpp] view plain copy  print?
  1. #include <unistd.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. int main()
  7. {
  8. int res = mkfifo("~/Test/PipeTest/my_fifo",0777);
  9. if(!res)
  10. printf("FIFO created\n");
  11. exit(EXIT_SUCCESS);
  12. }

运行结果:

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文件

[cpp] view plain copy  print?
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. 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。

测试代码:

[cpp] view plain copy  print?
  1. #include<unistd.h>
  2. #include<stdlib.h>
  3. #include<stdio.h>
  4. #include<string.h>
  5. #include<fcntl.h>
  6. #include<sys/types.h>
  7. #include<sys/stat.h>
  8. #define FIFO_NAME "/tmp/my_fifo"
  9. int main(int argc,char *argv[])
  10. {
  11. int res,i;
  12. int open_mode=0;
  13. if(argc < 2){
  14. fprintf(stderr,"Usage:%s<some combination of \
  15. O_RDONLY,O_WRONLY,O_NONBLOCK\n",*argv);
  16. exit(EXIT_FAILURE);
  17. }
  18. argv++;
  19. if(strncmp(*argv,"O_RDONLY",8)==0)open_mode|=O_RDONLY;
  20. if(strncmp(*argv,"O_WRONLY",8)==0)open_mode|=O_WRONLY;
  21. if(strncmp(*argv,"O_NONBLOCK",10)==0)open_mode|=O_NONBLOCK;
  22. for(i = 1;i < argc;++i)
  23. {
  24. argv++;
  25. if(*argv){
  26. if(strncmp(*argv,"O_RDONLY",8)==0)open_mode|=O_RDONLY;
  27. if(strncmp(*argv,"O_WRONLY",8)==0)open_mode|=O_WRONLY;
  28. if(strncmp(*argv,"O_NONBLOCK",10)==0)open_mode|=O_NONBLOCK;
  29. }
  30. }
  31. if(access(FIFO_NAME,F_OK)==-1){
  32. res=mkfifo(FIFO_NAME,0777);
  33. if(res!=0){
  34. fprintf(stderr,"Could not create fifo %s\n",FIFO_NAME);
  35. exit(EXIT_FAILURE);
  36. }
  37. }
  38. printf("process %d open FIFO with %d\n",getpid(),open_mode);
  39. res=open(FIFO_NAME,open_mode);
  40. printf("process %d result %d\n",getpid(),res);
  41. sleep(5);
  42. if(res!=-1)close(res);
  43. printf("process %d finished\n",getpid());
  44. exit(EXIT_SUCCESS);
  45. }

运行结果:

对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:

[cpp] view plain copy  print?
  1. //生产者程序
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. #include <string.h>
  6. #include <fcntl.h>
  7. #include <limits.h>
  8. #include <sys/types.h>
  9. #include <sys/stat.h>
  10. #define FIFO_NAME "/tmp/my_fifo"
  11. #define BUFFER_SIZE PIPE_BUF
  12. #define TEN_MSG (1024 * 1024 * 10)
  13. int main()
  14. {
  15. int pipe_fd;
  16. int res;
  17. int open_mode = O_WRONLY;
  18. int bytes_sent = 0;
  19. char buffer[BUFFER_SIZE + 1];
  20. printf("Productor Program beginning...\n");
  21. //检查FIFO文件是否存在
  22. if(access(FIFO_NAME, F_OK) == -1){
  23. res = mkfifo(FIFO_NAME, 0777);
  24. if(res != 0){
  25. fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME);
  26. exit(EXIT_FAILURE);
  27. }
  28. }
  29. printf("Process %d opening FIFO O_WRONLY\n", getpid());
  30. //打开FIFO文件
  31. pipe_fd = open(FIFO_NAME, open_mode);
  32. printf("Process %d result %d\n", getpid(), pipe_fd);
  33. if(pipe_fd != -1){
  34. while(bytes_sent < TEN_MSG){
  35. res = write(pipe_fd, buffer, BUFFER_SIZE); //向FIFO写入数据
  36. if(res == -1){
  37. fprintf(stderr, "Write error on pipe\n");
  38. exit(EXIT_FAILURE);
  39. }
  40. bytes_sent += res;
  41. }
  42. (void)close(pipe_fd);
  43. }
  44. else{
  45. exit(EXIT_FAILURE);
  46. }
  47. printf("Process %d finished\n", getpid());
  48. exit(EXIT_SUCCESS);
  49. }

消费者程序fork4.c:

[cpp] view plain copy  print?
  1. //消费者程序
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <fcntl.h>
  6. #include <limits.h>
  7. #include <sys/types.h>
  8. #include <sys/stat.h>
  9. #define FIFO_NAME "/tmp/my_fifo"
  10. #define BUFFER_SIZE PIPE_BUF
  11. int main()
  12. {
  13. int pipe_fd;
  14. int res;
  15. int open_mode = O_RDONLY;
  16. char buffer[BUFFER_SIZE + 1];
  17. int bytes = 0;
  18. printf("COnsumer Program beginning...");
  19. memset(buffer,'\0', sizeof(buffer));
  20. printf("Process %d opeining FIFO O_RDONLY\n", getpid());
  21. pipe_fd = open(FIFO_NAME, open_mode);
  22. printf("Process %d result %d\n", getpid(), pipe_fd);
  23. if (pipe_fd != -1)
  24. {
  25. do{
  26. res = read(pipe_fd, buffer, BUFFER_SIZE);
  27. bytes += res;
  28. }while(res > 0);
  29. close(pipe_fd);
  30. }
  31. else
  32. {
  33. exit(EXIT_FAILURE);
  34. }
  35. printf("Process %d finished, %d bytes read\n", getpid(), bytes);
  36. exit(EXIT_SUCCESS);
  37. }

运行结果:

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

删除FIFO文件

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

[cpp] view plain copy  print?
  1. #include <unistd.h>
  2. int unlink(const char *pathname);

关于unlink的详细内容参考:

unlink(2) - Linux man page

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

进程间通信-命名管道FIFO相关推荐

  1. linux 进程间通信 命名管道FIFO的原理与使用

    参考文章1:管道 pipe是什么?(进程通信的一种方式)(可以先大致了解管道) 参考文章2:FIFO(命名管道) FIFO常被称为命名管道,以区分管道(pipe).管道(pipe)只能用于" ...

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

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

  3. Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)

    整理自网络 Unix IPC包括:管道(pipe).命名管道(FIFO)与信号(Signal) 管道(pipe) 管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道 ...

  4. linux进程间通信:命名管道FIFO

    文章目录 FIFO 通信特点 系统调用接口 应用 拥有亲缘关系之间的进程通信 非亲缘关系进程之间的通信 总结 FIFO 通信特点 FIFO文件有文件名 可以像普通文件一样存储在文件系统之中 可以像普通 ...

  5. java mkfifo_命名管道FIFO和mkfifo函数

    进程间通信必须通过内核提供的通道,而且必须有一种办法在进程中标识内核提供的某个通道,前面讲过的匿名管道是用打开的文件描述符来标识的.如果要互相通信的几个进程没有从公共祖先那里继承文件描述符,它们怎么通 ...

  6. 进程间通信:管道和命名管道(FIFO)

    目录 概述 IPC 对象的持续性 什么是管道 读取外部程序的输出 将输出送往 popen 传递更多的数据 如何实现 popen pipe 调用 跨越 fork 调用管道 父进程和子进程 管道关闭后的读 ...

  7. 进程间通信:命名管道FIFO(2)

    一.命名管道 如果我们想在不相关的进程之间交换数据,可以用FIFO文件来完成这项工作,它通常也被称为命名管道.命名管道是一种特殊类型的文件,它在文件系统中以文件名的形式存在,但是它的行为却和我们已经见 ...

  8. 进程间通信--命名管道

    几个术语 二义性:当我们往一个管道里面写端写数据的时候,比如写一个hello的时候,当我们写到he的时候,读端就已经开始读取数据了,所以这是不对的,这就是二义性 临界资源:多个流可以访问的一个共同的存 ...

  9. linux系统编程之管道(三):命名管道FIFO和mkfifo函数

    进程间通信必须通过内核提供的通道,而且必须有一种办法在进程中标识内核提供的某个通道,前面讲过的匿名管道是用打开的文件描述符来标识的.如果要互相通信的几个进程没有从公共祖先那里继承文件描述符,它们怎么通 ...

  10. c语言程序实现进程的管道通信,C 进程间通信--命名管道通信代码实现及其原理图示...

    在将这个题目之前大家需要了解几个概念: 进程: 我们可以先看进程的定义:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础: ...

最新文章

  1. m5310模组数据上传至onenet_NBIOT模组M5310接入中国移动物联网开放平台示例文档
  2. Flex与.NET互操作(三):基于WebService的数据访问(下)
  3. opporeno3pro可以刷鸿蒙系统吗,华为P40放大招!鸿蒙系统+120Hz+徕卡五摄,明年2月发布!...
  4. vc 将已有项目打包成dll 并应用于其他项目_.NET混淆器 Dotfuscator使用教程:保护你的应用之存档报告文件...
  5. android开发之调皮的权限
  6. gromacs manual_GROMACS蛋白配体分子动力学模拟结果分析简要笔记
  7. 人脸对齐(十八)--Joint Face Alignment and 3D Face Reconstruction
  8. Mac 配置maven的环境变量
  9. 49. 学会分析与STL相关的编译器诊断信息
  10. 通过反汇编一个简单的C程序理解计算机是如何工作的
  11. 带滤镜拍照的app_这8款拍照修图APP,简直就是逼格神器!
  12. pb模型多batch_介绍Modern Batch和计算密集型编程模型
  13. HTML旅游景点网页作业制作——旅游中国11个页面(HTML+CSS+JavaScript)
  14. 【一起学习输入法】华宇拼音输入法开源版本解析(1)
  15. 微信公众平台开发之微信红包的实现
  16. HTML5+CSS3期末大作业:电影网站设计——黑色扁平的电影工作室静态网页 学生DW网页设计作业成品 web课程设计网页规划与设计 计算机毕设网页设计源码
  17. SOP是什么?SOP的作用是什么?如何编写SOP?
  18. 数学建模—MATLAB基本使用(一)
  19. 推荐:音速启动(快捷方式分类管理工具)
  20. 如何设计一个复杂的分布式爬虫系统?

热门文章

  1. Monitor线程同步
  2. H3C模拟器simware搭建总结
  3. set nocount on的疑问 set nocount on作用 set nocount on什么意思
  4. 黑色的php编辑器,五款常用的免费php编辑器推荐
  5. ecshop 邮件模板 html,给ecshop后台增加新的邮件模板
  6. c# 带返回值的action_C#基础知识之Func和Action学习
  7. python网络爬虫实践_第18,Python网络爬虫实践(1)
  8. 4.json字符串转换集json对象、json对象转换json字符串
  9. 均衡发展学校计算机室解说词,迎接省均衡发展学校解说词
  10. java 中如何临时保存某输入值_java笔记临时存放