『 Linux 』命名管道


命名管道与匿名管道

请添加图片描述

命名管道是管道的一种,数据流向为单向故被称为管道;

与匿名管道相同属于一种内存级文件;

区别如下:

  • 名字

    • 匿名管道

      没有名字,只存在于内存当中(类似内核缓冲区);

    • 命名管道

      有名字,基于文件系统,有对应的Inode以及对应属性;

      虽然存在Inode以及对应属性,但其对应的磁盘中的数据块指向为空,这表示命名管道不占用实际磁盘空间;

      其没有刷盘动作,因此对应数据不会刷新至磁盘中;

  • 通信进程

    • 匿名管道

      只适用于具有亲缘关系的进程进行通信;

    • 命名管道

      可适用于毫不相关的进程间的通信;

  • 数据流向

    • 匿名管道

      数据流向为单向;

    • 命名管道

      数据流向可以是单向也可以是双向(默认情况下的数据流向为单向);

  • 创建方式

    • 匿名管道

      在进程中调用pipe()系统调用接口创建管道文件;

      创建管道文件后调用fork()系统调用接口创建子进程从而为父子(有亲缘关系的)进程创建通信信道;

    • 命名管道

      在命令行中通常使用mkfifo命令创建管道;

      $ mkfifo myfifo;ll -i
      total 4
      2360390 prw-rw-r-- 1 _LJP _LJP    0 Jul 15 13:41 myfifo
      
      # 在bash中使用fifo命令创建一个名为myfifo的命名管道文件,并用 ll -i 选项查看命名管道的状态以及对应的Inode号
      

      结果来看命名管道存在对应的Inode;

      也可通过在进程中直接调用mkfifo()系统调用接口创建命名管道文件;

      NAME
             mkfifo  -  make  a  FIFO  special file (a named
             pipe)
      
      SYNOPSIS
             #include <sys/types.h>
             #include <sys/stat.h>
      
             int mkfifo(const char *pathname, mode_t mode);
      
      RETURN VALUE
             On  success mkfifo() returns 0.  In the case of
             an error, -1 is returned (in which case,  errno
             is set appropriately).
      
      

      调用mkfifo()系统调用接口时需要包含<sys/types.h>,<sys/stat.h>的头文件;

      传入参数pathnamemode分别代表 路径+管道文件名权限(匿名管道也是一个文件,需要具有权限) ;

      #include <sys/stat.h>
      #include <sys/types.h>
      
      #include <iostream>
      using namespace std;
      
      int main() {
        //
        mkfifo("./myfifo", 0664);
        return 0;
      }
      

      结果:

      $ ll -i;make;./mytest;make clean;ll -i
      total 8
      2361492 -rw-rw-r-- 1 _LJP _LJP  80 Jul 15 14:03 makefile
      2361488 -rw-rw-r-- 1 _LJP _LJP 147 Jul 15 14:02 test.cc
      g++ -o mytest test.cc -std=c++11 -g -Wall
      rm mytest
      total 8
      2361492 -rw-rw-r-- 1 _LJP _LJP  80 Jul 15 14:03 makefile
      2361494 prw-rw-r-- 1 _LJP _LJP   0 Jul 15 14:06 myfifo #命名管道文件被创建
      2361488 -rw-rw-r-- 1 _LJP _LJP 147 Jul 15 14:02 test.cc
      

      其中命名管道文件权限的最开头的p表示这个文件类型为一个管道文件(pipe);


命名管道特点

请添加图片描述

命名管道读写端在进行通信时将会互相等待;

  • 读端

    当管道内不存在数据时读端会进行阻塞并等待写端向管道内写入数据;

    # 读端
    $ cat myfifo 
    				# 进行阻塞等待写端写入
    #-------------------
    # 此时写端向管道内写入
    $ echo "hello world" > myfifo
    #-------------------
    # 读端
    $ cat myfifo 
    hello world 	# 停止阻塞将管道文件内数据进行读取
    
  • 写端

    当管道内的数据没有被读端读取时写端会进行阻塞等待读端将当前管道内的数据进行读取;

    # 写端
    $ echo "hello world" > myfifo
    					# 进行堵塞等待读端读取
    #-------------------
    # 此时打开读端读取命名管道文件内信息
    $ cat myfifo 
    hello world
    #-------------------
    $ echo "hello world" > myfifo
    $ 					# 写端停止阻塞
    

命名管道虽然具有Inode以及对应的属性,但其并不占用实际磁盘空间,只是用于数据传输,所以命名管道文件的大小始终为0;

  • 示例

    # [会话1]
    $ while :; do echo "hello myfifo" > myfifo ;sleep 1 ;done
    # ------------------------
    # [会话2]
    $ while :; do cat
     myfifo ;done
    hello myfifo
    hello myfifo
    hello myfifo
    hello myfifo
    ...
    # ------------------------
    # [会话3]
    $ ll
    total 8
    prw-rw-r-- 1 _LJP _LJP   0 Jul 15 14:39 myfifo
    $ ll
    total 8
    prw-rw-r-- 1 _LJP _LJP   0 Jul 15 14:39 myfifo
    

命名管道的理解

请添加图片描述

当两个不同的进程同时打开同一个文件时在内核当中也只是打开了一个文件;

文件系统与进程之间是同级关系且进行了解耦合;

  • 进程间通信的前提

    先让不同的进程看到同一份资源;

进程打开文件时文件系统不需要因为被打开而对文件进行拷贝复制等操作;

命名管道不占用实际磁盘空间,只是用于传输,不存在刷盘(写入磁盘)的动作,故实际上使用的也是该文件的内核缓冲区;

两个进程通过管道文件的 路径+文件名 再通过Inode编号来确定两个进程打开的是同一个管道文件( 路径+文件名 具有唯一性);


命名管道实现两个毫无关联的进程间通信

请添加图片描述

  • 准备

    • server.cc文件

      模拟实现服务端,用于管理命名管道文件,接收管道内文件并进行打印;

    • client.cc文件

      模拟实现客户端,用于向客户端写入数据;

    • comm.hpp文件

      用于包含所需头文件及简单声明定义所需常量等;

  • 具体思路

    两个毫不相关的进程,其中一个进程用来创建管道文件并维护管道文件,同时负责接收另一个进程向管道文件内写入的数据;

    另一个进程用来向管道文件内写入数据从而进行简单交互;

  • comm.hpp

    #include <fcntl.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    #include <cstdlib>
    #include <iostream>
    #include <string>
    
    #define PIPEFILE "./myfifo" // 管道文件文件名
    #define MODE 0666			// 管道文件权限
    
    #define SIZE 1024			// 缓冲区所需大小
    
    using namespace std;
    
    enum { FIFO_CREATE_ERR = 1, FIFO_DELETE_ERR, FIFO_OPEN_ERR }; // 枚举对应错误码
    
    class PipeInit {
     public:
      // 创建管道文件
      PipeInit() {
        int n = mkfifo(PIPEFILE, MODE);
        if (n == -1) {
          cerr << "CREATE PIPE FAIL" << endl;
          exit(FIFO_CREATE_ERR);
        }
      }
      // 销毁管道文件
      ~PipeInit() {
        int m = unlink(PIPEFILE);
        if (m == -1) {
          cerr << "DEL PIPE FAIL" << endl;
          exit(FIFO_DELETE_ERR);
        }
      }
    };
    

    创建一个管道类用于管理管道文件,即利用其构造函数创建管道文件,在退出后析构函数释放对应管道文件;

    enum枚举出错误信息常量;

  • server.cc

    
    #include "comm.hpp"
    
    // 服务端管理管道文件
    
    int main() {
      // 创建管道信道
      PipeInit myfifo;
    
      // 打开管道文件
      int fd = open(PIPEFILE, O_RDONLY);
      if (fd < 0) {
        cerr << "SERVER OPEN PIPE FAIL" << endl;
        exit(FIFO_OPEN_ERR);
      }
      cout << "Server open file done" << endl;
      // 开始进行通信
      char buffer[SIZE] = {0};  // 清空 当做字符串读取
      while (true) {
        int x = read(fd, buffer, sizeof(buffer));
        if (x > 0) {
          buffer[x] = '\0';
          cout << "server get a massage from client# ";
          cout << buffer << endl;
        }
        if (x == 0) {
          cout << "Client quit" << endl;
          break;
        }
      }
    
      // 结束通信关闭管道
      close(fd);
      return 0;
    }
    

    实例化一个对象依次构建对话对应所需信道;

    调用open()系统调用接口打开管道文件;

    创建buffer[]字符串充当缓冲区,并调用read()系统调用接口从命名管道文件中读取数据并进行打印;

    当写端被关闭时读端所调用的read()系统调用接口将返回0表示读到了文件末尾,作判断并结束程序;

    对话完毕后调用系统调用接口close()关闭对应文件描述符;

  • client.cc

    
    
    #include "comm.hpp"
    
    int main() {
      //
      int fd = open(PIPEFILE, O_WRONLY);
      if (fd < 0) {
        cerr << "CLIENT OPEN PIPE FAIL" << endl;
        exit(FIFO_OPEN_ERR);
      }
      cout << "Client open file done" << endl;
      string line;
      while (true) {
        cout << "Please Enter Your Massage@ ";
        getline(cin, line);
        write(fd, line.c_str(), line.size());
      }
      close(fd);
      return 0;
    }
    

    调用系统调用接口open()打开命名管道文件;

    创建缓冲区buffer[]并调用getline(cin,buffer)从键盘获取数据并存储至缓冲区中;

    调用write()系统调用接口将缓冲区中数据写入至命名管道文件中;

    对话完成后调用系统调用接口close()关闭对应文件描述符;

  • 17
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dio夹心小面包

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值