Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 415|回复: 0

[默认分类] Linux系统编程——进程间通信:命名管道(FIFO)

[复制链接]
  • TA的每日心情
    开心
    2021-12-13 21:45
  • 签到天数: 15 天

    [LV.4]偶尔看看III

    发表于 2018-5-30 12:12:46 | 显示全部楼层 |阅读模式


    命名管道的概述
    无名管道,由于没有名字,只能用于亲缘关系的进程间通信(更多详情,请看《无名管道》)。为了克服这个缺点,提出了命名管道(FIFO),也叫有名管道、FIFO 文件。


    命名管道(FIFO)不同于无名管道之处在于它提供了一个路径名与之关联,以 FIFO 的文件形式存在于文件系统中,这样,即使与 FIFO 的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过 FIFO 相互通信,因此,通过 FIFO 不相关的进程也能交换数据



    命名管道(FIFO)和无名管道(pipe)有一些特点是相同的,不一样的地方在于:

      1、FIFO 在文件系统中作为一个特殊的文件而存在,但 FIFO 中的内容却存放在内存中。
      2、当使用 FIFO 的进程退出后,FIFO 文件将继续保存在文件系统中以便以后使用。

      3、FIFO 有名字,不相关的进程可以通过打开命名管道进行通信。



    命名管道的创建
    所需头文件:

      #include <sys/types.h>
      #include <sys/stat.h>
      


    int mkfifo( const char *pathname, mode_t mode);
    功能:

      命名管道的创建。

    参数:

      pathname: 普通的路径名,也就是创建后 FIFO 的名字。

      mode: 文件的权限,与打开普通文件的 open() 函数中的 mode 参数相同,相关说明请点此链接

    返回值:

      成功:0
    失败:如果文件已经存在,则会出错且返回 -1。

      


    示例代码如下:
      
    1. #include <stdio.h>
    2. #include <sys/types.h>
    3. #include <sys/stat.h>
    4. int main(int argc, char *argv[])
    5. {
    6.         int ret;
    7.        
    8.         ret = mkfifo("my_fifo", 0666); // 创建命名管道
    9.         if(ret != 0){        // 出错
    10.                 perror("mkfifo");
    11.         }
    12.        
    13.         return 0;
    14. }
    复制代码


    运行结果如下:




    命名管道的默认操作
    后期的操作,把这个命名管道当做普通文件一样进行操作:open()、write()、read()、close()。但是,和无名管道一样,操作命名管道肯定要考虑默认情况下其阻塞特性。


    下面验证的是默认情况下的特点,即 open() 的时候没有指定非阻塞标志( O_NONBLOCK )。
    1)
    open() 以只读方式打开 FIFO 时,要阻塞到某个进程为写而打开此 FIFO
    open() 以只写方式打开 FIFO 时,要阻塞到某个进程为读而打开此 FIFO。
    简单一句话,只读等着只写,只写等着只读,只有两个都执行到,才会往下执行。


    只读端代码如下:
      
    1. #include <stdio.h>
    2. #include <string.h>
    3. #include <unistd.h>
    4. #include <sys/types.h>
    5. #include <sys/stat.h>
    6. #include <fcntl.h>
    7. int main(int argc, char *argv[])
    8. {
    9.         int fd;
    10.         int ret;
    11.        
    12.         ret = mkfifo("my_fifo", 0666);
    13.         if(ret != 0)
    14.         {
    15.                 perror("mkfifo");
    16.         }
    17.        
    18.         printf("before open\n");
    19.         fd = open("my_fifo", O_RDONLY);//等着只写
    20.         if(fd < 0)
    21.         {
    22.                 perror("open fifo");
    23.         }
    24.        
    25.         printf("after open\n");
    26.        
    27.         return 0;
    28. }
    复制代码


      
    只写端代码如下
      
    1. #include <stdio.h>
    2. #include <string.h>
    3. #include <unistd.h>
    4. #include <sys/types.h>
    5. #include <sys/stat.h>
    6. #include <fcntl.h>
    7. int main(int argc, char *argv[])
    8. {
    9.         int fd;
    10.         int ret;
    11.        
    12.         ret = mkfifo("my_fifo", 0666);
    13.         if(ret != 0)
    14.         {
    15.                 perror("mkfifo");
    16.         }
    17.        
    18.         printf("before open\n");
    19.         fd = open("my_fifo", O_WRONLY); //等着只读
    20.         if(fd < 0)
    21.         {
    22.                 perror("open fifo");
    23.         }
    24.        
    25.         printf("after open\n");
    26.        
    27.         return 0;
    28. }
    复制代码


    大家开启两个终端,分别编译以上代码,读端程序和写端程序各自运行,如下图,大家自行验证其特点,因为光看结果图是没有效果,大家需要分析其运行过程是如何变化。




    如果大家不想在 open() 的时候阻塞,我们可以以可读可写方式打开 FIFO 文件,这样 open() 函数就不会阻塞。
      
    1. // 可读可写方式打开
    2. int fd = open("my_fifo", O_RDWR);
    复制代码


      
    2)假如 FIFO 里没有数据,调用 read() 函数从 FIFO 里读数据时 read() 也会阻塞。这个特点和无名管道是一样的。


    写端代码如下:
      
    1. #include <stdio.h>
    2. #include <string.h>
    3. #include <unistd.h>
    4. #include <sys/types.h>
    5. #include <sys/stat.h>
    6. #include <fcntl.h>
    7. int main(int argc, char *argv[])
    8. {
    9.         int fd;
    10.         int ret;
    11.        
    12.         ret = mkfifo("my_fifo", 0666);//创建命名管道
    13.         if(ret != 0)
    14.         {
    15.                 perror("mkfifo");
    16.         }
    17.        
    18.         printf("before open\n");
    19.         fd = open("my_fifo", O_WRONLY); //等着只读
    20.         if(fd < 0)
    21.         {
    22.                 perror("open fifo");
    23.         }
    24.         printf("after open\n");
    25.        
    26.         printf("before write\n");
    27.         // 5s后才往命名管道写数据,没数据前,读端read()阻塞
    28.         sleep(5);
    29.         char send[100] = "Hello Mike";
    30.         write(fd, send, strlen(send));
    31.         printf("write to my_fifo buf=%s\n", send);
    32.        
    33.         return 0;
    34. }
    复制代码


    读端代码如下:
      
      
    1. #include <stdio.h>
    2. #include <string.h>
    3. #include <unistd.h>
    4. #include <sys/types.h>
    5. #include <sys/stat.h>
    6. #include <fcntl.h>
    7. int main(int argc, char *argv[])
    8. {
    9.         int fd;
    10.         int ret;
    11.        
    12.         ret = mkfifo("my_fifo", 0666); //创建命名管道
    13.         if(ret != 0)
    14.         {
    15.                 perror("mkfifo");
    16.         }
    17.        
    18.         printf("before open\n");
    19.         fd = open("my_fifo", O_RDONLY);//等着只写
    20.         if(fd < 0)
    21.         {
    22.                 perror("open fifo");
    23.         }
    24.         printf("after open\n");
    25.        
    26.         printf("before read\n");
    27.         char recv[100] = {0};
    28.        
    29.         //读数据,命名管道没数据时会阻塞,有数据时就取出来
    30.         read(fd, recv, sizeof(recv));
    31.         printf("read from my_fifo buf=[%s]\n", recv);
    32.        
    33.         return 0;
    34. }
    复制代码


    请根据下图自行编译运行验证:
      




    3)通信过程中若写进程先退出了,就算命名管道里没有数据,调用 read() 函数从 FIFO 里读数据时不阻塞;若写进程又重新运行,则调用 read() 函数从 FIFO 里读数据时又恢复阻塞。


    写端代码如下:
      
    1. #include <stdio.h>
    2. #include <string.h>
    3. #include <unistd.h>
    4. #include <sys/types.h>
    5. #include <sys/stat.h>
    6. #include <fcntl.h>
    7. int main(int argc, char *argv[])
    8. {
    9.         int fd;
    10.         int ret;
    11.        
    12.         ret = mkfifo("my_fifo", 0666); // 创建命名管道
    13.         if(ret != 0)
    14.         {
    15.                 perror("mkfifo");
    16.         }
    17.        
    18.         fd = open("my_fifo", O_WRONLY); // 等着只读
    19.         if(fd < 0)
    20.         {
    21.                 perror("open fifo");
    22.         }
    23.        
    24.         char send[100] = "Hello Mike";
    25.         write(fd, send, strlen(send));        //写数据
    26.         printf("write to my_fifo buf=%s\n",send);
    27.        
    28.         while(1); // 阻塞,保证读写进程保持着通信过程
    29.        
    30.         return 0;
    31. }
    复制代码


    读端代码如下:

      
    1. #include <stdio.h>
    2. #include <string.h>
    3. #include <unistd.h>
    4. #include <sys/types.h>
    5. #include <sys/stat.h>
    6. #include <fcntl.h>
    7. int main(int argc, char *argv[])
    8. {
    9.         int fd;
    10.         int ret;
    11.        
    12.         ret = mkfifo("my_fifo", 0666);// 创建命名管道
    13.         if(ret != 0)
    14.         {
    15.                 perror("mkfifo");
    16.         }
    17.        
    18.         fd = open("my_fifo", O_RDONLY);// 等着只写
    19.         if(fd < 0)
    20.         {
    21.                 perror("open fifo");
    22.         }
    23.        
    24.         while(1)
    25.         {
    26.                 char recv[100] = {0};
    27.                 read(fd, recv, sizeof(recv)); // 读数据
    28.                 printf("read from my_fifo buf=[%s]\n",recv);
    29.                 sleep(1);
    30.         }
    31.        
    32.         return 0;
    33. }
    复制代码


    请根据下图自行编译运行验证:




    5)通信过程中,读进程退出后,写进程向命名管道内写数据时,写进程也会(收到 SIGPIPE 信号)退出。

    6)调用 write() 函数向 FIFO 里写数据,当缓冲区已满时 write() 也会阻塞。



    5)和 6)这两个特点和无名管道是一样的,这里不再验证,详情请看《无名管道》


    命名管道非阻塞标志操作
    命名管道可以以非阻塞标志(O_NONBLOCK)方式打开:
      
    1. fd = open("my_fifo", O_WRONLY|O_NONBLOCK);
    2. fd = open("my_fifo", O_RDONLY|O_NONBLOCK);
    复制代码

      


    非阻塞标志(O_NONBLOCK)打开的命名管道有以下特点:
    1、先以只读方式打开,如果没有进程已经为写而打开一个 FIFO, 只读 open() 成功,并且 open() 不阻塞。

    2、先以只写方式打开,如果没有进程已经为读而打开一个 FIFO,只写 open() 将出错返回 -1。

    3、read()、write() 读写命名管道中读数据时不阻塞。



    请根据以下代码自行编译运行验证。


    写端代码如下:
      
    1. #include <stdio.h>
    2. #include <string.h>
    3. #include <unistd.h>
    4. #include <sys/types.h>
    5. #include <sys/stat.h>
    6. #include <fcntl.h>
    7. int main(int argc, char *argv[])
    8. {
    9.         int fd;
    10.         int ret;
    11.        
    12.         ret = mkfifo("my_fifo", 0666); // 创建命名管道
    13.         if(ret != 0)
    14.         {
    15.                 perror("mkfifo");
    16.         }
    17.        
    18.         // 只写并指定非阻塞方式打开
    19.         fd = open("my_fifo", O_WRONLY|O_NONBLOCK);
    20.         if(fd<0)
    21.         {
    22.                 perror("open fifo");
    23.         }
    24.        
    25.         char send[100] = "Hello Mike";
    26.         write(fd, send, strlen(send));
    27.         printf("write to my_fifo buf=%s\n",send);
    28.         while(1);
    29.        
    30.         return 0;
    31. }
    复制代码


    读端代码如下:
      
      
    1. #include <stdio.h>
    2. #include <string.h>
    3. #include <unistd.h>
    4. #include <sys/types.h>
    5. #include <sys/stat.h>
    6. #include <fcntl.h>
    7. int main(int argc, char *argv[])
    8. {
    9.         int fd;
    10.         int ret;
    11.        
    12.         ret = mkfifo("my_fifo", 0666); // 创建命名管道
    13.         if(ret != 0)
    14.         {
    15.                 perror("mkfifo");
    16.         }
    17.        
    18.         // 只读并指定非阻塞方式打开
    19.         fd = open("my_fifo", O_RDONLY|O_NONBLOCK);
    20.         if(fd < 0)
    21.         {
    22.                 perror("open fifo");
    23.         }
    24.        
    25.         while(1)
    26.         {
    27.                 char recv[100] = {0};
    28.                 read(fd, recv, sizeof(recv));
    29.                 printf("read from my_fifo buf=[%s]\n",recv);
    30.                 sleep(1);
    31.         }
    32.        
    33.         return 0;
    34. }
    复制代码


    本教程示例代码下载请点此处。
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-31 08:02 , Processed in 0.354389 second(s), 37 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表