【Linux】进程间通信 -- 命名管道 | mkfifo调用

news2024/12/29 10:29:51

  • 小实验
    • 1.我们首先创建两个文件
    • 2.我们使用创建命名管道的命令mkfifo
    • 3.开始执行
  • 在语言层面使用命名管道实现通信
    • 1.创建命名管道-函数mkfifo
    • 2.写入端代码
    • 3.读取端代码
    • 小细节:

小实验

1.我们首先创建两个文件

client.cpp:

#include <iostream>
using namespace std;
int main()
{
    cout<<"hello client"<<endl;
    return 0;
}

server.cpp:

#include <iostream>
using namespace std;
int main()
{
    cout<<"hello server"<<endl;
    return 0;
}

然后创建Makefile使得我们更方便的去编译:

.PHONY:all
all:server client

server:server.cpp
	g++ -o $@ $^ -std=c++11
client:client.cpp
	g++ -o $@ $^ -std=c++11

.PHONY:clean
clean:
	rm -f server client

这样我们使用一条指令编译两个文件:
在这里插入图片描述

2.我们使用创建命名管道的命令mkfifo

在这里插入图片描述

[AMY@VM-12-15-centos lesson_15]$ mkfifo named_pipe
[AMY@VM-12-15-centos lesson_15]$ ll -i
total 12
664845 -rw-rw-r-- 1 AMY AMY 102 Jun 29 15:17 client.cpp
664846 -rw-rw-r-- 1 AMY AMY 157 Jun 29 15:19 Makefile
658324 prw-rw-r-- 1 AMY AMY   0 Jun 29 15:29 named_pipe
664847 -rw-rw-r-- 1 AMY AMY 102 Jun 29 15:17 server.cpp
[AMY@VM-12-15-centos lesson_15]$ 

我们可以发现我们创建的named_pipe是以p开头而且有自己独立的inode,说明它是一个独立的管道文件

3.开始执行

我们执行下面脚本,主要的功能就是使用echo循环输出hello world!到管道文件named_pipe

cnt=0; while :; do echo "hello world! -> $cnt"; let cnt++; sleep 1; done > named_pipe

在这里插入图片描述
我们发现,我执行输出到管道文件,但是管道文件里面的文件大小始终没有变化且为0

我们再使用cat命令将管道文件named_pipe的内容,重定向输出到显示器,能看到脚本循环输出的值,且是从0开始的
在这里插入图片描述

命名管道是如何做到让不同的进程,先看到了同一份资源?可以让不同的进程打开指定名称(路径+文件名)的同一个文件
原因是:路径+文件名=唯一性(匿名管道是通过继承)

在语言层面使用命名管道实现通信

1.创建命名管道-函数mkfifo

在这里插入图片描述
comm.hpp创建一个管道文件

#pragma once

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

#define NAMED_PIPE "/tmp/my_named_pipe"
//创建指定路径上的命名管道
bool createFifo(const std::string &path)
{
    umask(0);//将权限掩码设置为0
    int n = mkfifo(path.c_str(), 0600);
    if (n == 0)
        return true;
    else
    {
        std::cout << "errno: " << errno << " err string: " << strerror(errno) << std::endl;
        return false;
    }
}
//删除指定路径上的命名管道
void removeFifo(const std::string &path)
{
    int n = unlink(path.c_str());
    assert(n == 0);
    (void)n;
}

解释:函数createFifo的作用是创建一个匿名管道,而它传入的参数path则是创建的命名管道的路径。函数调用mkfifo(path.c_str(), 0600)来创建一个命名管道。mkfifo()是一个系统调用,用于创建一个新的命名管道。它的第一个参数是一个指向管道路径的字符串,第二个参数是一个表示权限位的八进制数。在这个例子中,权限被设置为"0600",表示只有管道的所有者可以读写,其他用户没有任何访问权限。

返回值:如果mkfifo()调用成功,它将返回0,函数将返回true表示管道创建成功。如果mkfifo()调用失败,它将返回-1,并设置全局变量errno来指示错误的类型。函数将打印错误信息并返回false表示管道创建失败。strerror(errno)用来获取对应错误码的错误信息。

2.写入端代码

client.cpp:

#include "comm.hpp"
int main()
{
    std::cout << "client begin" << std::endl;
    int wfd = open(NAMED_PIPE, O_WRONLY);//仅以写入模式打开
    std::cout << "client end" << std::endl;
    if(wfd < 0) exit(1); 

    //write
    char buffer[1024];
    while(true)
    {
        std::cout << "Please Say# ";
        fgets(buffer, sizeof(buffer), stdin); // abcd\n
        if(strlen(buffer) > 0) buffer[strlen(buffer)-1] = 0;
        ssize_t n = write(wfd, buffer, strlen(buffer));
        assert(n == strlen(buffer));
        (void)n;
    }

    close(wfd);
    return 0;
}

代码功能:从标准输入stdin读取用户输入,并将其写入到一个命名管道中
解释:
fgets(buffer, sizeof(buffer), stdin);用于从标准输入读取一行文本。fgets()是一个标准库函数,用于从指定的文件流中读取一行文本。它的第一个参数是一个字符数组,用于存储读取的文本,第二个参数是数组的长度,第三个参数是要读取的文件流。在本例中,fgets()将从标准输入(stdin)中读取一行文本,并将其存储到buffer数组中。

if(strlen(buffer) > 0) buffer[strlen(buffer)-1] = 0;用于去掉输入文本中的换行符\n,因为fgets()函数会将输入文本中的换行符也读取进来。如果输入文本的长度大于0,则将最后一个字符设为0,以去掉换行符。

ssize_t n = write(wfd, buffer, strlen(buffer));用于将去掉换行符的输入文本写入到命名管道中。write()是一个系统调用,用于向文件描述符中写入数据。它的第一个参数是文件描述符,第二个参数是要写入的数据,第三个参数是要写入的数据长度。在本例中,wfd是命名管道的文件描述符,buffer是要写入的数据,strlen(buffer)是要写入的数据长度。

assert(n == strlen(buffer));用于确保写入操作成功,并且写入的数据长度与预期的长度相同。assert()是一个断言宏,用于在条件不满足时引发一个断言错误。在本例中,如果写入操作失败或者写入的数据长度与预期的长度不同,assert()将引发一个断言错误,并停止程序的执行。(void)n;用于避免编译器警告未使用变量n的情况。(因为Linux默认编译是release版本的,而release版本的assert会失效)

3.读取端代码

server.cpp:

#include "comm.hpp"

int main()
{
	//创建管道并检查是否创建好
    bool r = createFifo(NAMED_PIPE);
    assert(r);
    (void)r;

    std::cout << "server begin" << std::endl;
    int rfd = open(NAMED_PIPE, O_RDONLY);//打开文件以只读方式
    std::cout << "server end" << std::endl;
    if(rfd < 0) exit(1);

    //read
    char buffer[1024];
    while(true)
    {
        ssize_t s = read(rfd, buffer, sizeof(buffer)-1);
        if(s > 0)
        {
            buffer[s] = 0;
            std::cout << "client->server# " << buffer << std::endl;
        }
        else if(s == 0)
        {
            std::cout << "client quit, me too!" << std::endl;
            break;
        }
        else
        {
            std::cout << "err string: " << strerror(errno) << std::endl;
            break;
        }
    }

    close(rfd);
    removeFifo(NAMED_PIPE);
    return 0;
}

代码功能:使用一个无限循环,从一个命名管道中读取数据,并将读取到的数据打印到标准输出stdout
解释:
ssize_t s = read(rfd, buffer, sizeof(buffer)-1);用于从命名管道中读取数据。read()是一个系统调用,用于从文件描述符中读取数据。它的第一个参数是文件描述符,第二个参数是用于存储读取数据的缓冲区,第三个参数是缓冲区的长度。在本例中,rfd是命名管道的文件描述符,buffer是用于存储读取数据的缓冲区,sizeof(buffer)-1是缓冲区的长度。

if(s > 0)用于判断是否成功读取到数据。如果s大于0,则说明成功读取到数据。buffer[s] = 0;用于将读取到的数据转换为字符串,并在字符串末尾添加一个空字符,以便将其打印到标准输出上。std::cout << "client->server# " << buffer << std::endl;用于将读取到的数据打印到标准输出上。

else if(s == 0)用于判断是否已经读取到了命名管道的末尾。如果s等于0,则说明已经读取到了命名管道的末尾,即客户端已经关闭了管道。此时程序将打印一条消息并退出循环。

else用于处理读取数据时发生的错误。如果s小于0,则说明读取数据时发生了错误。strerror(errno)用于获取错误信息,并将其打印到标准输出上。break用于退出循环。

小细节:

    std::cout << "server begin" << std::endl;
    int rfd = open(NAMED_PIPE, O_RDONLY);
    std::cout << "server end" << std::endl;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

我们发现./server运行后输出"server begin"之后到open阻塞了,而是要等./client后才会继续执行,因为读取端打开要等写入端也打开才会继续运行,也就是说两者都要同时打开才能运行这个管道


如有错误或者不清楚的地方欢迎私信或者评论指出🚀🚀

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/736832.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《向量数据库指南》——什么是比较 Embedding?

目录 比较 Embedding 准备工作 示例 0:Marlon Brando 示例 1:国王与王后 示例 2:Apple,水果还是公司 欢迎回到向量数据库 101 系列教程。 之前的教程中,我们介绍了非结构化数据、向量数据库和 Milvus——全球最受欢迎的开源向量数据库。我们还简单介绍了 Embedding …

windows提权总结

文章目录 windows基础知识提权总结基础知识提权思路metasplit 提权反弹shell提权windows系统配置错误提权系统服务权限配置错误不带引号的服务路径提权注册键 AlwaysInstallElevated 本地dll劫持提权第三方提权sqlserver提权mysql提权MOF提权G6FTP提权 绕过UAC bypassuac远程终…

前端|HTML

参考视频&#xff1a;黑马程序员前端Html5教程&#xff0c;前端必备基础 目录 &#x1f4da;web标准 &#x1f4da;HTML认识 &#x1f407;HTML的概念 &#x1f407;开发工具 &#x1f407;HTML标签基础 ⭐️HTML骨架标签 ⭐️HTML元素标签分类 ⭐️HTML标签关系&#…

一款可源码交付的Java可视化建模规则引擎

规则引擎是一种根据规则中包含的指定过滤条件&#xff0c;判断其能否匹配运行时刻的实时条件来执行规则中所规定的动作的引擎。 规则引擎由推理引擎发展而来&#xff0c;是一种嵌入在应用程序中的组件&#xff0c;实现了将业务决策从应用程序代码中分离出来&#xff0c;并使用预…

FPGA纯verilog实现10G UDP协议栈,XGMII接口UltraScale GTY驱动,提供工程源码和技术支持

目录 1、前言2、我这里已有的UDP方案3、该UDP协议栈性能4、详细设计方案SFPSGMII收发接口模块AXIS FIFOUDP协议栈UltraScale FPGAs Transceivers Wizard GTY 5、vivado工程6、上板调试验证并演示准备工作查看ARPUDP数据回环测试 7、福利&#xff1a;工程代码的获取 1、前言 目…

SpringBoot原理分析 | 安全框架:Security

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; Security Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架&#xff1b;提供一组可以在Spring应用上下文中配置的Be…

使用作用域函数简化您的 Kotlin 代码:释放对象的力量

使用作用域函数简化您的 Kotlin 代码&#xff1a;释放对象的力量 欢迎&#xff0c;亲爱的 Kotlin 爱好者&#xff01;厌倦了在处理对象时反复书写相同的代码吗&#xff1f;别担心&#xff01;Kotlin 的作用域函数来拯救你&#xff0c;为你的编码之旅增添优雅的色彩。在这篇有趣…

华为认证 | 7月底这门HCIA认证即将发布!

华为认证HCIA-Cloud Service V3.5&#xff08;中文版&#xff09;预计将于2023年7月31日正式对外发布。 为了帮助您做好学习、培训和考试计划&#xff0c;现进行预发布通知&#xff0c;请您关注。 01 发布概述 基于“平台生态”战略&#xff0c;围绕“云-管-端”协同的新ICT技…

小程序学习(三):小程序的宿主环境

1.什么是宿主环境 宿主环境指的是程序运行所必须的依赖环境。例如:Android系统和IOS系统是两个不同的宿主环境。安卓版的微信App是不能在IOS环境下运行的,所以Android是暗转软件的宿主环境,脱离宿主环境的软件是没有任何意义的! 2.小程序的宿主环境 小程序借助宿主环境提供的能…

(4)【轨迹优化篇】线性加权轨迹代价损失评分,轨迹多选一方法--DWA_planner、Open_Planner、lexicographic_planner

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录前言1、设计代价损失函数进行评分、线性加权法、迹评分轨迹多选一的轨迹优化设计思想&#xff08;1&#xff09;设计代价函数进行…

适用于企业级工作负载的混合闪存存储

NetApp FAS9500、FAS8700、FAS8300&#xff1a;适用于企业级工作负载的 FAS 拥有企业级应用程序和私有云的客户通常部署这些系统来作为架构基础。通过与主要超大规模云提供商的简单集成创建统一的混合云 借助 NetApp FAS 按需扩展性能 客户将这些平台描述为“重型”&#xff0…

2023年开源社执行委员会介绍

总起 开源社的各项工作主要分为内部事务、对外事务与专项工作三大类。整体而言&#xff0c;基础设施、财务、法律与成员发展等工作较为偏向于内部事务&#xff0c;而活动、媒体、社区合作、顾问委员会服务、国际接轨等工作偏向于对外联络与展现&#xff0c;而教育、公益、硬件、…

基于Tensorflow来重现GPT v1模型

OpenAI推出的ChatGPT模型让我们看到了通用人工智能的发展潜力&#xff0c;我也找了GPT的相关论文来进行研究。OpenAI在2017年的论文Improving Language Understanding by Generative Pre-Training提出了GPT的第一个版本&#xff0c;我也基于这个论文来用Tensorflow进行了复现。…

红外雨量计(光学雨量传感器)在监测降雨量的应用

红外雨量计&#xff08;光学雨量传感器&#xff09;在监测降雨量的应用 红外雨量计是一种基于红外光学原理的降雨量监测设备。它利用红外传感器检测雨滴落在传感器上时所产生的反射光信号&#xff0c;根据信号的强弱和持续时间计算出降雨强度和累计降雨量。 红外雨量计在监测降…

FITC Biotin(134759-22-1),5-FAM azide(510758-23-3),荧光素标记试剂的特点

Part1-----FITC-Biotin&#xff0c;134759-22-1 基础产品数据&#xff08;Basic Product Data&#xff09;&#xff1a; CAS号&#xff1a;134759-22-1 中文名&#xff1a;异硫氰酸荧光素标记生物素 英文名&#xff1a;FITC-Biotin&#xff0c;Fluorescein Biotin&#xff0c;…

vue element select下拉框树形多选

components 文件下新建 SelectTree文件 index.vue SelectTree index.vue <!--* 下拉树形选择 组件--> <template><el-select ref"select" style"min-width: 260px" :value"value" v-model"valueName" collapse-tags :…

Python应用实例(一)外星人入侵(七)

外星人入侵&#xff08;七&#xff09; 1.射杀外星人1.1 检测子弹与外星人的碰撞1.2 为测试创建大子弹1.3 生成新的外星人群1.4 提高子弹的速度1.5 重构_update_bullets() 2.结束游戏2.1 检测外星人和飞船碰撞2.2 响应外星人和飞船碰撞2.3 有外星人到达屏幕底端2.4 游戏结束 3.…

毫米波雷达(三):实操

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、福相一致性校准二、速度解模糊其他 一、福相一致性校准 多波束天线通道幅相一致性校正及实现 https://www.docin.com/p-2640129630.html 二、速度解模糊 一种…

SpringBoot的@ConfigurationProperties、@Autowired、@Conditional注解

目录 1. ConfigurationProperties EnableConfigurationProperties Autowired注解1.1 configuration自定义配置参数自动补全功能 2. Conditional注解 1. ConfigurationProperties EnableConfigurationProperties Autowired注解 在resources/application.properties文件中&a…

map、foreach 和 for ,多角度对比

for 1.访问元素和操作方式------通过索引访问数组元素&#xff0c;并可以执行任意操作。 const numbers [1, 2, 3, 4, 5];// for循环示例 for (let i 0; i < numbers.length; i) {console.log("Number (for loop): " numbers[i]); } 2.索引控制--------通过…