【Linux】命名管道使用示例-代码实现

news2024/11/21 0:32:13

文章目录

  • 1 管道基础知识复习(可直接跳转代码实现)
    • 1.1 管道的读写规则
    • 1.2 管道的特点
  • 2 命名管道
    • 2.1 命名管道本质
    • 2.2 创建命名管道
      • 2.2.1 在命令行创建:
      • 2.2.2 在程序中调用函数创建
    • 2.3 命名管道和匿名管道的区别
    • 2.4 命名管道的打开规则
  • 3 代码分解实现
    • 3.1 makefile书写
      • 3.1.1 测试编译
    • 3.2 comm.cpp的编写
      • 3.2.1 权限设置:0666表示->
      • 3.2.2 uint32_t
    • 3.3 server.cc
    • 3.4 client.cc

1 管道基础知识复习(可直接跳转代码实现)

1.1 管道的读写规则

首先我们复习以下管道的一些特性:

  • 当没有数据可读时:

    • O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。
    • O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。
  • 当管道满的时候

    • O_NONBLOCK disable: write调用阻塞,直到有进程读走数据
    • O_NONBLOCK enable:调用返回-1,errno值为EAGAIN
  • 如果所有管道写端对应的文件描述符被关闭,则read返回0

  • 如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程退出

  • 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。

  • 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。

1.2 管道的特点

  • 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
  • 管道提供流式服务
  • 一般而言,进程退出,管道释放,所以管道的生命周期随进程
  • 一般而言,内核会对管道操作进行同步与互斥
  • 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道
    在这里插入图片描述

2 命名管道

2.1 命名管道本质

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

2.2 创建命名管道

2.2.1 在命令行创建:

$ mkfifo filename

2.2.2 在程序中调用函数创建

int mkfifo(const char *filename,mode_t mode);
//其中,第一个参数是文件名,第二个参数是权限设置
int main(int argc, char *argv[])
{
mkfifo("p2", 0644);
return 0;
}

2.3 命名管道和匿名管道的区别

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

2.4 命名管道的打开规则

  • 如果当前打开操作是为读而打开FIFO时
    • O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO
    • O_NONBLOCK enable:立刻返回成功
  • 如果当前打开操作是为写而打开FIFO时
    • O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
    • O_NONBLOCK enable:立刻返回失败,错误码为ENXIO

3 代码分解实现

要使用管道进行通信,我们需要两个"人",一个是客户端,一个是服务器.且为了方便代码的编写,我们将两者公用的结构体和头文件放在同一个hpp文件中

3.1 makefile书写

//要让两个文件同时编译,我们可以定义一个all,然后all分别依赖两个文件:
.PHONY:all
all: server client

server:server.cc
	g++ -o $@ $^ -std=c++11
client:client.cc
	g++ -o $@ $^ -lncurses -std=c++11
.PHONY:clean
clean:
	rm -rf server client

3.1.1 测试编译

在这里插入图片描述
完美通过

3.2 comm.cpp的编写

#pragma once
#include<iostream>
#include<string>
#define NUM 1024

//为了保证管道通信的双方都可以通过命名管道文件进行通信:
const std::string filename = "./fifo";// 即在当前目录下创建fifo文件
//由于我们需要调用linux系统调用,所以必然是c\cpp混编
//而linux系统调用不认识string类,到时候我们务必要转换为C字符串类型
uint32_t mode = 0666;//设置权限

3.2.1 权限设置:0666表示->

666代表
该文件拥有者对该文件拥有读写的权限但是没有操作的权限
该文件拥有者所在组的其他成员对该文件拥有读写的权限但是没有操作的权限
其他用户组的成员对该文件也拥有读写权限但是没有操作的权限

777 代表
该文件拥有者对该文件拥有读写操作的权限
该文件拥有者所在组的其他成员对该文件拥有读写操作的权限
其他用户组的成员对该文件也拥有读写操作权限

但是要注意,我们真正使用的时候,权限会被当时的掩码减去,以下是例子:

一般而言,umask为022,也就是说,对于当前用户没有拿掉权限,group用户和other用户都被拿走了w权限,所以此时如果用户进行创建目录和文件的时候,默认权限是会进行如下的减法操作:
新建文件:666-022=644;
新建目录:777-022=755.

所以当我们真正要为这个文件赋予666权限的时候,要注意将当前环境的掩码设置为0

3.2.2 uint32_t

我们直接转到定义:
在这里插入图片描述
可以看到,其实就是一个无符号整型,不过系统帮我们封装了
我们这里为了规范,也直接使用unit32_t来定义

3.3 server.cc

接下来如果有函数不太会用,可以使用man手册:

man 1 命令
man 2 系统调用
man 3 库函数

接下来是该代码最重头的地方:

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

int main()
{
	//1. 创建管道文件,今天我们只需要创建一次
	umask(0);//原因上面说了.且这个设置并不影响系统的默认配置,只会影响当前进程的掩码设置
	int n = mkfifo(fifoname.c_str(), mode);//linux系统调用不认识string类,务必要转换为C字符串类型
	//用n接收返回值是为了检查其调用合法性
	if(n != 0)//正常调用应该返回0
    {
        std::cout << errno << " : " << strerror(errno) << std::endl;
        return 1;
    }
    std::cout << "create fifo file success" << std::endl;//此时输出一个提示方便我们调试

	//2.让服务器端直接开启管道文件(以只写方式),也是需要以一个值来接收判断合法性
	int rfd = write(filename.c_str(),O_RDONLY);
	if(rfd < 0 )
    {
        std::cout << errno << " : " << strerror(errno) << std::endl;
        return 2;
    }

	// 3. 正常通信
    char buffer[NUM];
    while(true)
    {
        buffer[0] = 0;//每次都要把buffer置空(直需要把第一个数据设为\0即可)
        ssize_t n = read(rfd, buffer, sizeof(buffer) - 1);//服务端是读取数据,返回值是读取到的字节个数
        if(n > 0)
        {
            buffer[n] = 0;//一个字符是一个字节,所以读取到了n个字节的话,在第n+1个字节的位置放置0,来切断句子
            printf("%c", buffer[0]);
            fflush(stdout);//立马刷新缓冲区
        }
        else if(n == 0)
        {
            std::cout << "client quit, me too" << std::endl;
            //什么都没有读到,说明客户端已经退出了
            break;
        }
        else 
        {
            std::cout << errno << " : " << strerror(errno) << std::endl;
            break;
        }
    }
    // 关闭不要的fd
    close(rfd);

    unlink(fifoname.c_str());//删除这个管道文件,以便下次使用
	return 0;
}

3.4 client.cc

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

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

    // 可以进行常规通信了
    char buffer[NUM];
    while(true)
    {
        // std::cout << "请输入你的消息# ";
        // char *msg = fgets(buffer, sizeof(buffer), stdin);
        // assert(msg);
        // (void)msg;
        // int c = getch();
        // std::cout << c << std::endl;
        // if(c == -1) continue;

        system("stty raw");
        int c = getchar();
        system("stty -raw");

        //std::cout << c << std::endl;
        //sleep(1);

        //buffer[strlen(buffer) - 1] = 0;
        // abcde\n\0
        // 012345
        //if(strcasecmp(buffer, "quit") == 0) break;

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

    close(wfd);

    return 0;
}

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

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

相关文章

计算值组成原理 作业8

作业8 题量: 28 满分: 100 作答时间:04-20 09:40至04-26 23:59 100分 一. 单选题&#xff08;共14题&#xff0c;32分&#xff09; 1. (单选题, 2分)计算机硬件能直接执行的只有_____。 A. 算法语言B. 汇编语…

2023-04-22 学习记录--C/C++-数组

合抱之木&#xff0c;生于毫末&#xff1b;九层之台&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。&#x1f4aa;&#x1f3fb; 一、定义一维数组 ⭐️ &#xff08;一&#xff09;、初识 格式 &#x1f308;&#xff1a;数组元素类型 数组名[数组元素个数]…

Java每日一练(20230423)

目录 1. 数组元素统计 ※ 2. 杨辉三角 II &#x1f31f; 3. 二进制求和 &#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 1. 数组元素统计 定义一个长度为5的数组arr1&a…

【三十天精通Vue 3】第十六天 Vue 3 的虚拟 DOM 原理详解

引言 Vue 3 的虚拟 DOM 是一种用于优化 Vue 应用程序性能的技术。它通过将组件实例转换为虚拟 DOM&#xff0c;并在组件更新时递归地更新虚拟 DOM&#xff0c;以达到高效的渲染性能。在 Vue 3 中&#xff0c;虚拟 DOM 树由 VNode 组成&#xff0c;VNode 是虚拟 DOM 的基本单元…

PTA L1-096 谁管谁叫爹 (20 分)

《咱俩谁管谁叫爹》是网上一首搞笑饶舌歌曲&#xff0c;来源于东北酒桌上的助兴游戏。现在我们把这个游戏的难度拔高一点&#xff0c;多耗一些智商。 不妨设游戏中的两个人为 A 和 B。游戏开始后&#xff0c;两人同时报出两个整数 N A N_A NA​​ 和 N B ​ N_B​ NB​​ 。判…

C语言函数大全-- n 开头的函数

C语言函数大全 本篇介绍C语言函数大全-- n 开头的函数 1. nan 1.1 函数说明 函数声明函数功能double nan(const char *tagp);用于返回一个表示 NaN&#xff08;非数值&#xff09;的 double 类型数字 参数&#xff1a; tagp &#xff1a; 指向字符串的指针&#xff1b;用于…

Tomcat 配置与部署

http 协议就是 http 客户端和 http 服务器之间通信的协议 , 而Tomcat 就是 java 圈子中最广泛使用的 http 服务器. 下载Tomcat Tomcat官网 Tomcat 的版本 , 和后续的 servlet 版本是强相关的 , 此处使用 tomcat 8 , 对应的 servlet 就是 3.1 下载一个 zip 压缩包解压缩即可 T…

探索【Stable-Diffusion WEBUI】的插件:骨骼姿态(OpenPose)

文章目录 &#xff08;零&#xff09;前言&#xff08;一&#xff09;骨骼姿态&#xff08;OpenPose&#xff09;系列插件&#xff08;二&#xff09;插件&#xff1a;PoseX&#xff08;三&#xff09;插件&#xff1a;Depth Lib&#xff08;四&#xff09;插件&#xff1a;3D …

Spring之IOC和DI入门案例

IOC和DI入门案例 1. IOC入门案例1.1 门案例思路分析1.2 实现步骤1.3 实现代码1.4 运行结果 2. DI入门案例2.1 DI入门案例思路分析2.2 实现步骤2.3 实现代码2.4 图解演示 1. IOC入门案例 问题导入 <bean>标签中id属性和class属性的作用是什么&#xff1f; 1.1 门案例思…

金三银四总计面试碰壁15次,作为一个27岁的测试工程师.....

3年测试经验原来什么都不是&#xff0c;只是给你的简历上画了一笔&#xff0c;一直觉得经验多&#xff0c;无论在哪都能找到满意的工作&#xff0c;但是现实却是给我打了一个大巴掌&#xff01;事后也不会给糖的那种... 先说一下自己的个人情况&#xff0c;普通二本计算机专业…

023:Mapbox GL加载mp4视频文件

第023个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中加载MP4视频文件。一个视频源。 “urls”值是一个数组。 对于数组中的每个 URL,将创建一个视频元素源。 要支持跨浏览器的视频,请提供多种格式的 URL。“坐标”数组包含按顺时针顺序列出的视频角的 [longi…

【MYSQL】数据库和表的基本操作

目录 1.mysql的工作图&#xff1a; 2.连接mysql服务器 3.mysql的配置文件 4.数据库的操作 5.表的操作 1.mysql的工作图&#xff1a; mysql是一个应用层服务&#xff0c;需要使用安装的mysql客户端&#xff08;也叫mysql&#xff09;连接mysql服务器&#xff08;也叫mysq…

Sa-Token源码简单阅读

一.权限登录模块包括几个基本子模块&#xff1a; 1.登录。 实现方式大致为&#xff1a;先检验用户名密码是否正确&#xff0c;如正确则在缓存中存入用户信息&#xff08;一般必须要有用户标识和访问token&#xff0c;或再加一些附加信息如用户的角色权限&#xff09;&#xf…

国内外4款主流ERP系统评测,哪款最好用?

一、ERP系统的概念 ERP系统&#xff0c;是针对通用各个企业特点研发的ERP软件。由于行业产品结构复杂&#xff0c;导致原料种类众多&#xff0c;制造工艺复杂&#xff0c;外加客户、供应商、物流等不确定因素&#xff0c;传统手工、表格、纸质作业模式难以应对复杂状况&#x…

设计模式之责任链模式(C++)

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 一、责任链模式是什么&#xff1f; 责任链模式是一种行为型的软件设计模式&#xff0c;对象内存在对下家的引用&#xff0c;层层连…

【世界读书日】2023年通信好书推荐

今天是世界读书日&#xff08;4月23日&#xff09;。按照老规矩&#xff0c;小编给大家推荐一些通信类的优秀书籍。 过去一年&#xff0c;通信行业的关注热点&#xff0c;主要是&#xff1a;5G-Advanced&#xff08;5.5G&#xff09;、算力网络、东数西算、6G、卫星互联网、智…

历史上的今天大事件查询工具推荐 - 历史上的今天 API

引言 历史上的今天&#xff0c;总会有一些特别的事件发生&#xff0c;这些事件对人类的发展产生了深远的影响。想要了解这些事件&#xff0c;往往需要花费大量的时间和精力去查阅历史资料。但现在&#xff0c;有了历史上的今天 API&#xff0c;一切变得方便了许多。 如果你对…

交友项目【根据id查询单条动态发布评论查询评论列表】

目录 1&#xff1a;根据id查询单条动态 1.1&#xff1a;接口分析 1.2&#xff1a;流程分析 1.3&#xff1a;代码实现 2&#xff1a;发布评论 2.1&#xff1a;接口分析 2.2&#xff1a;流程分析 2.3&#xff1a;代码实现 3&#xff1a;查询评论列表 3.1&#xff1a;接…

Python导出含有中文名文件解决方案

使用Python开发过程中有用到需要导出文件的功能 异常代码 # 代码片段 def return_workbook(self, workbook, model_code, x_io):name “税单.xls”workbook.close()res HttpResponse()res["Content-Type"] "application/octet-stream"res["Conte…

Python 基础(九):列表

❤️ 博客主页&#xff1a;水滴技术 &#x1f338; 订阅专栏&#xff1a;Python 入门核心技术 &#x1f680; 支持水滴&#xff1a;点赞&#x1f44d; 收藏⭐ 留言&#x1f4ac; 文章目录 一、声明列表二、访问列表元素三、修改列表元素四、添加列表元素4.1、在列表末尾添加元…