当前位置: 首页 > article >正文

Linux 命名管道

目录

一、前言

二、命名管道原理

三、优化代码


一、前言

我们之前讲的管道只能有血缘关系的进程进行通信。这个管道是匿名管道。

这节博客,我们将为大家讲解命名管道。

二、命名管道原理

什么是命名管

我们可以直接使用下面的命令去创建命名管道

mkfifo myfifo 

它可以在自己当前目录下创建一个管道。 

我们能看到它的权限以p开头,所以这是个管道文件。命名管道文件,它在磁盘中并没有数据,它更多的只是一种符号。因为管道文件,不需要把数据输入到磁盘,是内存级文件,内存大小一直是0。我们来测试一下。

我们输入数据到管道文件中,我们看到处于阻塞状态,因为我们还没有读取数据。

我们创建了两个窗口,我们看到当我cat读取数据时,写端也不堵塞了。

 我们写入shell脚本,写端在不断写入管道中,读端不断输出。

 

这是两个毫不相关的进程,没有血缘关系,但他们能进行通信,因为存在命名管道。

我们之前写到过,进程通信的本质是让两个不同的进程看到同一份资源。

那我们怎么知道两个进程看到的是同一份资源呢?也就是说打开同一个文件呢?

当我们在同一路径下看到同一个文件名,则这是同一份资源(同一个文件)

因为路径+文件名具有唯一性。

我们接下来用代码测试一下我们的原理。

三、编码测试

上面的函数可以去创建一个管道文件,第一个参数是路径,第二个是管道的权限是什么,如果成功是0,否则返回-1。

这样我们就创建了一个管道

 当我们要删除一个管道的时候可以用unlink接口,我们在讲解软链接的时候为大家提到过。

我们写一个具体的代码介绍该原理。

做一个服务端和用户端,用户端用来写入数据,服务端用来读取数据。

我们把所有的头文件和宏定义全部放到我们自己创建的头文件中。

comm.hpp

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

#define FIFO_FILE "./myfifo"
#define MODE 0666
enum{
  CREAT_FIFO_ERR = 1,
  OPEN_ERR = 2,
  DELETE_ERR = 3,

};

servers.cpp

#include "comm.hpp"

using namespace std;
    
int main()
{
    //创建管道
    int n=mkfifo(FIFO_FILE,MODE);
    if(n==-1)
    {
        perror("mkfifo");
        exit(CREAT_FIFO_ERR);
    }
    //打开管道文件
    int fd = open(FIFO_FILE,O_RDONLY);
    if(fd<0)
    {
        perror("open");
        exit(OPEN_ERR);
    }
    cout <<"server open file sueccess" <<endl;
    //读取文件
    while(1)
    {
        char buffer[1024];
        int n = read(fd,buffer,sizeof(buffer));
        if(n > 0)
        {
            buffer[n]=0;
            cout<<"客户端接收到信息:"<<buffer<<endl;
        }
        else if(n == 0)
        {
            cout<<"客户端读完所有信息"<<endl;
            break;
        }
        else break;
    }
    //收尾工作
    close(fd);
    int m = unlink(FIFO_FILE);
    if(m==-1)
    {
        perror("unlink");
        exit(DELETE_ERR);
    }

    return 0;

}

client.cc 

#include "comm.hpp"

using namespace std;
int main()
{
    int fd = open(FIFO_FILE,O_CREAT|O_WRONLY);
    if(fd < 0)
    {
        perror("open");
        exit(OPEN_ERR);
    }

    cout<<"open file success!"<<endl;
    string line;
    while(1)
    {
        cout<<"please enter @:";
        getline(cin,line);
        write(fd,line.c_str(),line.size());
    }
    close(fd);
    return 0;
}

运行结果如下:

当只运行读端会阻塞,等待输入

 

当我们运行客户端写入后,同时出现打开文件成功。

接下来我们从客户端往里面写入数据,我们能看到一一对应。

 

最终我们我们关闭客户端的同时,由于关闭了写端,但是读端没有关闭,就会读入0,最终服务端的代码逻辑会检测到这个0,从而结束进程。

 同样的,如果我们先关闭了服务端,那么就会杀掉客户端的进程

三、优化代码

 我们创建一个类,把管道初始化和清理放到类里面。

 定一个Init对象,会自己调用其构造函数和析构函数。

 客户端代码不变。


http://www.kler.cn/a/561509.html

相关文章:

  • Docker 搭建 Nginx 服务器
  • DeepSeek 助力 Vue 开发:打造丝滑的分割线(Divider)
  • 2024年第十五届蓝桥杯青少 图形化编程(Scratch)省赛中级组真题——截取递增数
  • RTSP中RTP/RTCP协议栈、NTP同步及QoS机制
  • Ollama部署本地大模型DeepSeek-R1-Distill-Llama-70B
  • 如何成为Apache Doris的贡献者
  • Windows 中的启动项如何打开?管理电脑启动程序的三种方法
  • 13. MySQL 事务基础知识(详细说明实操剖析)
  • C++编程指南17 - 使用 RAII(资源获取即初始化),避免直接调用 lock()/unlock()
  • 医疗UI的特殊法则:复杂数据可视化的“零错误”设计守则
  • DeepSeek赋能机器人革命:从推理引擎到行业落地的全栈技术实践
  • #Oracle 学习进阶路线-进阶篇:高可用、性能调优与云原生的实战突破
  • el-select滚动获取下拉数据;el-select滚动加载
  • 【云原生实战】DevOps基础与实战项目
  • 浅谈HTTP及HTTPS协议
  • 全域旅游景区导览系统:赋能智慧旅游生态,破解行业核心难题
  • AWS CLI将读取器实例添加到Amazon Aurora集群
  • AI大模型-提示工程学习笔记17—程序辅助语言模型
  • 博途V16画面管理、用户管理与文本和图形列表
  • 希尔排序:突破插入排序的局限