【Linux】命名管道

文章目录

  • 命名管道
      • 一、命名管道的原理
      • 二、命名管道的创建
          • 命令行中创建
          • 程序中创建 - mkfifo函数:
      • 三、命名管道的使用
          • 命名管道实现server&client通信
      • 四、匿名管道与命名管道的区别

命名管道

如果涉及到在文件系统中创建一个有名的管道,那么就是在使用命名管道。

一、命名管道的原理

  • 管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
  • 如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
  • 命名管道是一种特殊类型的文件

注意:
普通文件是很难做到通信的,即便做到通信也无法解决一些安全问题。
命名管道和匿名管道一样,都是内存文件,只不过命名管道在磁盘有一个简单的映像,但这个映像的大小永远为0,因为命名管道和匿名管道都不会将通信数据刷新到磁盘当中。

二、命名管道的创建

命令行中创建

在命令行中,可以使用mkfifo命令创建命名管道。mkfifo命令的语法如下:

mkfifo [OPTION]... NAME...

其中,OPTION是可选的参数,而NAME是要创建的命名管道的名称。以下是一个简单的示例:

mkfifo fifo

【Linux】命名管道_第1张图片

这会在当前目录下创建一个名为 fifo 的命名管道。命名管道的创建后,可以像文件一样对待,可以通过其他命令或程序来读取和写入,但是。

程序中创建 - mkfifo函数:

mkfifo函数是一个库函数,它是由C标准库提供的,而不是直接调用底层操作系统的系统调用。在C语言中,可以使用mkfifo系统调用来在程序中创建命名管道。该系统调用的函数原型如下:

#include 
#include 

int mkfifo(const char *pathname, mode_t mode);
  • pathname 参数是命名管道的路径和名称。
  • mode 参数是权限位,用于指定文件权限。
    当然,实际上创建出来文件的权限值还会受到umask(文件默认掩码)的影响,实际创建出来文件的权限为:mode&(~umask)。umask的默认值一般为0002,如果我们设置mode值为0666时,则实际创建出来文件的权限为0664。

三、命名管道的使用

命名管道实现server&client通信

实现服务端(server)和客户端(client)之间的通信之前,我们需要先让服务端运行起来,让服务端运行后创建一个命名管道文件,然后客户端再以读的方式打开该命名管道文件,之后服务端就可以从该命名管道当中读取客户端发来的通信信息了。

服务端的代码,server.cpp 如下:

#include "comm.h"

using namespace std;

int main()
{
    int n = mkfifo(FILENAME, 0666);
    if (n < 0)
    {
        cerr << "mkfifo failed, errno: " << errno << ", errstring:" << strerror(errno) << endl;
        return 1;
    }
    cout << "mkfifo success..." << endl;

    int rfd = open(FILENAME, O_RDONLY);
    if (rfd < 0)
    {
        cerr << "errno: " << errno << ", errstring:" << strerror(errno) << endl;
        return 2;
    }
    cout << "open fifo success..." << endl;

    char buffer[1024];
    while (true)
    {
        ssize_t s = read(rfd, buffer, sizeof(buffer) - 1);
        if (s > 0)
        {
            buffer[s] = '\0';
            cout << "Client say# " << buffer << endl;
        }
    }

    close(rfd);
    cout << "close fifo success..." << endl;

    return 0;
}

客户端的代码,如下:

#include "comm.h"

using namespace std;

int main()
{
    int wfd = open(FILENAME, O_WRONLY);
    if (wfd < 0)
    {
        cerr << "errno: " << errno << ", errstring:" << strerror(errno) << endl;
        return 1;
    }

    string message;
    while (true)
    {
        cout << "Please Enter# ";
        getline(cin, message);

        ssize_t s = write(wfd, message.c_str(), message.size());
        if (s < 0)
        {
            cerr << "errno: " << errno << ", errstring:" << strerror(errno) << endl;
            return 2;
        }
    }

    close(wfd);
    std::cout << "close fifo success..." << std::endl;

    return 0;
}

让客户端和服务端包含同一个头文件,该头文件当中提供这个共用的命名管道文件的文件名和共用的头文件。

// comm.h
#pragma once

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
  
#define FILENAME "myfifo" //让客户端和服务端使用同一个命名管道

先将服务端进程运行起来,之后我们就能在客户端看到这个已经被创建的命名管道文件:
【Linux】命名管道_第2张图片

【Linux】命名管道_第3张图片

使用ps axj搭配grep来查询这两个进程:

ps axj | head -1 && ps axj| grep -E '\./server|\./client' | grep -v grep

【Linux】命名管道_第4张图片

发现这两个进程确实是两个毫不相关的进程,因为它们的PID和PPID都不相同。也就证明了,命名管道是可以实现两个毫不相关进程之间的通信的。

有个小问题是,如果跑完一次想要再运行,命名管道文件mypipe还依然存在,会引发报错程序直接退出:
请添加图片描述


优化一下server.cpp:

#include "comm.h"

using namespace std;

bool MakeFifo()
{
   int n = mkfifo(FILENAME, 0666);
   if (n < 0)
   {
       cerr << "mkfifo failed, errno: " << errno << ", errstring:" << strerror(errno) << endl;
       return false;
   }
   cout << "mkfifo success..." << endl;
   return true;
}

int main()
{
Start:
   int rfd = open(FILENAME, O_RDONLY);
   if (rfd < 0) // 优化:如果open失败,就创建fifo管道文件,然后回到start再open
   {
       cerr << "errno: " << errno << ", errstring:" << strerror(errno) << endl;
       if (MakeFifo())
           goto Start;
       else
           return 1;
   }
   cout << "open fifo success..." << endl;

   char buffer[1024];
   while (true)
   {
       ssize_t s = read(rfd, buffer, sizeof(buffer) - 1);
       if (s > 0)
       {
           buffer[s] = '\0';
           cout << "Client say# " << buffer << endl;
       }
   }

   close(rfd);
   cout << "close fifo success..." << endl;

   return 0;
}

四、匿名管道与命名管道的区别

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

你可能感兴趣的:(Linux系统编程,linux,网络,运维,cpp,c语言)