命名管道:简单案例实现

📟作者主页:慢热的陕西人

🌴专栏链接:Linux

📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言

本博客主要内容讲解了什么是命名管道,匿名管道和命名管道的区别,并且实现了一个案例来实践操作

Linux命名管道

1.概念

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

2.创建一个命名管道

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

    mkfifo filename

    image-20231128174723565

    见见猪跑:

    向管道写入:echo hello > fifo:我们发现程序卡住了,细节是我们查看fifo的文件的大小的时候,它却是0,因为我们知道管道是内存级文件,他的内容不会刷新到磁盘,所以我们看到的文件大小就是0,当我们cat < fifo也就是将fifo中的内容重定向输出到cat中打印出了hello

image-20231128193005159

那么上面的操作,是通过一个管道文件,让两个无关的进程实现的进程间的通信。那么我们知道进程间的通信就是要让两个进程看到同一个文件,那么这里我们是如何做到同一个文件呢?答案是路径,文件的唯一性就是由路径表示的!


那么我们到底是如何使用命名管道实现两个进程间的通信的:

①我们需要创建一个管道文件

这里我们需要用到一个系统接口mkfifo

image-20231128204130327

#include<iostream>
#include<sys/stat.h>
#include<sys/types.h>
#include<string.h>
#include<cerrno>

#include "comm.hpp"


using namespace std;
int main()
{
//1.创建管道文件,我们今天只需要创建一次
int n  = mkfifo(filename.c_str(),mode);
if(n != 0) //创建失败
{
  cout << errno << " : "<< strerror(errno)<<  endl;
  return 1;
}

return 0;
}

运行一下:

我们发现确实创建了文件:但是文件的权限却不是我们想要的0666,其实我们一下就能看出来原因是我们的umask不为零影响到了结果

image-20231128204311535

我们查看一个操作系统的接口:umask

image-20231128204715405

#include<iostream>
#include<sys/stat.h>
#include<sys/types.h>
#include<string.h>
#include<cerrno>

#include "comm.hpp"


using namespace std;
int main()
{
//1.创建管道文件,我们今天只需要创建一次
umask(0); //只影响当前进程的umask
int n  = mkfifo(filename.c_str(),mode);
if(n != 0) //创建失败
{
  cout << errno << " : "<< strerror(errno)<<  endl;
  return 1;
}

return 0;
}

运行一下看看:
image-20231128204930740

②让读写端进程分别按照自己的需求打开文件

 //1.不需要创建管道文件,我只需要打开对应的文件即可!
 int wfd = open(filename.c_str(), O_WRONLY);
 if(wfd < 0)
 {
     cerr << errno << " : "<< strerror(errno)<<  endl;
     return 1;
 }



 //1.创建管道文件,我们今天只需要创建一次
 umask(0); //只影响当前进程的umask
 int n  = mkfifo(filename.c_str(),mode);
 if(n != 0) //创建失败
 {
     cout << errno << " : "<< strerror(errno)<<  endl;
     return 1;
 }

③开始通信

//3.开始通信
 char buffer[NUM];
 while(true)
 {
     buffer[0] = 0; //初始化第一个位置为\0
     //读文件
     ssize_t n = read(rfd, buffer, sizeof(buffer) - 1);

     if(n > 0)
     {
         //读成功了
         buffer[n] = 0;
         printf("%s\n", buffer);
         fflush(stdout);
     }
     else if(n == 0)
     {
         //写端关闭
         cout << "client quit, me too" << endl;
         break;
     }
     else
     {
         //错误
         cout << errno << " : "<< strerror(errno)<<  endl;
         return 1;
     }
 }



 //2.开始通信
 char buffer[NUM];

 while(true)
 {
     cout << "请输入要写入的信息#";
     char* msg = fgets(buffer, sizeof(buffer), stdin);
     assert(msg);
     (void)msg;

     buffer[strlen(buffer) - 1] = 0;


     //当我们写入quit的时候退出写端
     if(strcasecmp(buffer, "quit") == 0) break;

     ssize_t n = write(wfd, buffer, strlen(buffer));
     assert(n >= 0); 
     (void)n;
 }

运行结果:

image-20231128223524668

3.匿名管道与命名管道的区别

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

4.我们实现一个即时的输入输出

就是我们输入管道的时候不用回车,而是选用输入一个字符就从管道输出:

我们要在client.cc端改变其向文件写入的方式,我们读取到字符c中,然后没输入一个字符就同步;

    while(true)
    {

        system("stty raw");  // 使终端驱动处于一次一字符模式
        char c = getchar();
        system("stty cooked");// 使终端驱动回到一次一行模式 

        ssize_t n = write(wfd, &c, sizeof(char));
        assert(n >= 0); 
        (void)n;
    }

运行结果:

打印结果

5.完整代码

client.cc

#include<iostream>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
#include<cerrno>
#include<cassert>
#include"comm.hpp"


using namespace std;
int main()
{
    //1.不需要创建管道文件,我只需要打开对应的文件即可!
    int wfd = open(filename.c_str(), O_WRONLY);
    if(wfd < 0)
    {
        cerr << errno << " : "<< strerror(errno)<<  endl;
        return 1;
    }

    //2.开始通信
    char buffer[NUM];

    while(true)
    {

        // cout << "请输入要写入的信息#";
        // char* msg = fgets(buffer, sizeof(buffer), stdin);
        // assert(msg);
        // (void)msg;

        // buffer[strlen(buffer) - 1] = 0;

        system("stty raw");  // 使终端驱动处于一次一字符模式
        char c = getchar();
        system("stty cooked");// 使终端驱动回到一次一行模式 


        // //当我们写入quit的时候退出写端
        // if(strcasecmp(buffer, "quit") == 0) break;

        ssize_t n = write(wfd, &c, sizeof(char));
        assert(n >= 0); 
        (void)n;
    }

    return 0;
}

server.cc

#include<iostream>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
#include<cerrno>

#include "comm.hpp"


using namespace std;
int main()
{
    //1.创建管道文件,我们今天只需要创建一次
    umask(0); //只影响当前进程的umask
    int n = mkfifo(filename.c_str(),mode);
    if(n != 0) //创建失败
    {
        cout << errno << " : "<< strerror(errno)<<  endl;
        return 1;
    }

    cout << "创建文件成功!"<< endl;

    //2.打开文件
    int rfd = open(filename.c_str(), O_RDONLY);
    if(rfd < 0) //打开错误
    {
        cout << errno << " : "<< strerror(errno)<<  endl;
        return 2;
    }

    cout << "打开管道成功" << endl;

    //3.开始通信
    char buffer[NUM];
    while(true)
    {
        buffer[0] = 0; //初始化第一个位置为\0
        //读文件
        ssize_t n = read(rfd, buffer, sizeof(buffer) - 1);

        if(n > 0)
        {
            //读成功了
            printf("%c", buffer[0]);
            fflush(stdout);
        }
        else if(n == 0)
        {
            //写端关闭
            cout << "client quit, me too" << endl;
            break;
        }
        else
        {
            //错误
            cout << errno << " : "<< strerror(errno)<<  endl;
            return 1;
        }
    }


    //4.关闭文件
    close(rfd);

    unlink(filename.c_str());
    return 0;
}

comm.hpp

#pragma once

#include <iostream>
#include <string>


using namespace std;


#define NUM 65535 //管道缓冲区大小


string filename = "./fifo"; //文件名
mode_t mode = 0666; //打开方式

到这本篇博客的内容就到此结束了。
如果觉得本篇博客内容对你有所帮助的话,可以点赞,收藏,顺便关注一下!
如果文章内容有错误,欢迎在评论区指正

在这里插入图片描述

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 16
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小米睡不醒.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值