【Linux】-进程间通信-命名管道文件(没有关系的进程间进行通信),以及写一个日志模板

在这里插入图片描述
💖作者:小树苗渴望变成参天大树🎈
🎉作者宣言:认真写好每一篇博客💤
🎊作者gitee:gitee✨
💞作者专栏:C语言,数据结构初阶,Linux,C++ 动态规划算法🎄
如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧!

文章目录

  • 前言
  • 一、演示命名管道文件
  • 二、理解
  • 三,编写代码
  • 四、编写日志代码
    • 4.1代码
    • 4.2 介绍知识点
    • 4.3 看加入日志的效果
    • 五、总结


前言

今天博主开始给大家讲解一下命名管道文件,就是相比较于匿名管道,是有名字的,命名管道文件的特点进行没有任何关系的进程间进行通信,他的原理几乎和匿名管道差不多,但是还是有差距,博主在匿名管道哪里没讲开,因为需要两者对比的区分开才更有效果,他进行通信的时候和文件操作差不多,话不多说,开始进入正文讲解

讲解逻辑:

  1. 演示命名管道文件
  2. 理解命名管道文件
  3. 编码实现

一、演示命名管道文件

我们在命令行当中通过指令来实现两个进程之间的通信,因为指令也是程序,每个指令互相都是没有关系的,我们使用mkfifo这个指令来创建命名管道文件

mkfifo myfifo

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

通过上面的演示,我们echo往管道文件写入,但是他是阻塞的,等到有人来读取他,他才没有阻塞,所以我们得出来一个结论,我们管道文件只有在读写端都接入的时候,才会被打开。这也是管道文件的一个特性,符合同步互斥机制。

管道文件的演示很简单,接下来我们直接来谈谈他的理解

二、理解

大家应该很奇怪,为什么叫理解,不讲原理吗,原因是我们的命名管道和匿名管道原理差不多,但是周边有点小不同,需要大家去理解一下。

  1. 如果两个不同的进程打开同一个文件的时候,os会打开几个文件?答案是一个,文件系统只需要管理一份就好,相信细心的小伙伴发现了·,博主将匿名的,叫做匿名管道,将命名的,叫做命名管道文件,说明在本质上我们的匿名管道不是我们用户看到的文件一样,

在这里插入图片描述

可以简单理解命名管道文件在普通文件和匿名管道之间,但是效率还是匿名管道强一点,因为少了一层拷贝,我们可以通过下面的脚本来观察,管道文件确实是不刷盘的。
在这里插入图片描述
博主使用一直往管道问价那里面然后读取,我们使用 ls -l | grep myfifo 去查看,发现文件大小一直为0,所以内容不在硬盘上。

  1. 进程间通信的本质是,让不同的进程看到不同的资源,有了上面的理解1,这份资源也是os给的,而且管道文件也不在硬盘上,博主这是云服务器,当我们在后台开机重启的时候,这个管道文件应该也随之消失,他的生命周期是随着内核的,而不是随着进程的,通过上面的演示也可以看出来,我们的程序结束了,他依旧存在,还有大家别重新打开xshell,然后发现管道文件还在,难道博主说的不对,你重新打开xshell只是bash这个进程重新加载了,就好比你关闭电脑上的一个软件重新启动一样,大家别搞错了,我们的Linux系统是一直在运行的。
  2. 通过理解2,我们创建了管道文件,是为了让不同进程看到同一份资源,也就是看到这个管道文件,那你是怎么知道这两个进程打开的是同一个文件呢??答案是同路径下同文件名,路径天生是唯一的,根据文件系统的知识,我们同路径下的文件名也是唯一的,所以通过这两点–唯一性,让不同的进程打开的是同一个文件。至此我们建立了命名管道文件。

通过上面的理解,大家应该理解了命名管道文件,原理和匿名很相似,所以接下来博主直接带大家来编写大家,让大家更好的理解

三,编写代码

既然是命名管道文件,肯定不能像创建普通文件一样使用mkdir去创建,按照我们之前的经验,准确来说是进程间通信的本质,我们认为这个命名管道文件应该是由操作系统去创建,我们来看看怎么在代码中创建管道:
在这里插入图片描述

第一个参数是创建管道的路径和文件名,第二个参数文件

用完想删除管道文件,使用unlink函数
在这里插入图片描述

传刚才文件的路径和文件名就可以了

serverPipe.cc

#include "fifo.hpp"

//此进程是服务端,给客户端发送内容的
int main()
{
    //创建管道文件
    int n=mkfifo(PATHNAME,MODE);
    if(n<0)
    {
        perror("mkfifo:");
        exit(EXIT_MKFIFO);
    }
    
    //打开管道,等待读入方打开管道,这个才会打开
    int fd=open(PATHNAME,O_WRONLY);
    if(fd<0)
    {
        perror("open:");
        exit(EXIT_OPEN);
    }

    //进行通信
    string s;
    while(true)
    {
        cout << "Please Enter@ ";
        getline(cin, s);
        write(fd,s.c_str(),s.size());
    }

    //关闭管道
    close(fd);


    //删除管道文件
    int m=unlink(PATHNAME);
    if(m<0)
    {
        perror("unlink:");
        exit(EXIT_UNLINK);
    }
    return 0;
}

clientPipe.cc

#include "fifo.hpp"

//此进程是客户端,接收服务端发过来的消息
int main()
{
    //创建管道文件另一个进程已经帮助我们做好了,我们拿来使用就好了

    int fd=open(PATHNAME,O_RDONLY);
    if(fd<0)
    {
        perror("open:");
        exit(EXIT_OPEN);
    }

    char buffer[N];
    while(true)
    {
        buffer[0]=0;
        size_t n=read(fd,buffer,sizeof(buffer));
        if (n > 0)
        {
            buffer[n] = 0;
            cout << "server say# " << buffer << endl;
        }
        else if (n == 0)
        {
            printf("server quit, me too!, error string: %s, error code: %d\n", strerror(errno), errno);
            break;
        }
        else
            break;
    }
    close(fd);
    return 0;
}


fifo.hpp

#pragma once

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

using namespace std;

#define PATHNAME "./myfifo"  //创建管道文件的路径和文件名
#define MODE 0664  //权限
#define N 1024

#define EXIT_MKFIFO -1
#define EXIT_OPEN -2
#define EXIT_UNLINK -3

在这里插入图片描述
这个理解起来不难,大家也看到效果了。读端会等待写端,所以也是具有同步互斥机制的

但是代码不够优雅,博主选择将创建管道和删除管道,放到一个类里面,都放在头文件里面:

fifo.hpp

#pragma once

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

using namespace std;

#define PATHNAME "./myfifo"  //创建管道文件的路径和文件名
#define MODE 0664  //权限
#define N 1024

#define EXIT_MKFIFO -1
#define EXIT_OPEN -2
#define EXIT_UNLINK -3


class Init
{
public:
    Init()
    {
        int n=mkfifo(PATHNAME,MODE);
        if(n<0)
        {
            perror("mkfifo:");
            exit(EXIT_MKFIFO);
        }
    }
    ~Init()
    {
        int m=unlink(PATHNAME);
        if(m<0)
        {
            perror("unlink:");
            exit(EXIT_UNLINK);
        }
    }
};

四、编写日志代码

我们发现每次写代码打印错误信息的时候,都是直接写的,而现在博主就交给你们一个办法,以后再学习中都可以使用到,而且这个以后去公司的时候,也不需要你自己去写的,我们了解这个原理就可以了,这里会介绍一些不常用的函数接口,接下来博主就来介绍一下什么是日志。

4.1代码

我们的日志是有等级之分的,先简单的来区分一下:

#define INFO 0  //常规消息
#define WARNING 1 //警告消息
#define ERROR 2 //错误消息
#define FATAL 3 //非常严重消息
#define DEBUG 4 //调试消息

日志一般都会有时间的,所以要重点介绍一下获取时间的函数,其次日志等级是认为规定,所以传参要传等级,我们打印的格式以及要打印多少个信息也是我们认为控制的,所以最好传可变参数,这样就可以很好的控制了。类似于这样:
log(INFO, "server open file done, error string: %s, error code: %d", strerror(errno), errno);

博主先把代码粘贴出来,再来解释博主认为你们不理解的地方

#include<iostream>
#include<string>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
using namespace std;

//日志等级
#define INFO 0  //常规消息
#define WARNING 1 //警告消息
#define ERROR 2 //错误消息
#define FATAL 3 //非常严重消息
#define DEBUG 4 //调试消息

//打印方式
#define SCREEN  0
#define ONEFILE 1
#define CLASSFILE 2

#define SIZE 1024
#define LOGTXT "log.txt" //默认的存放所有日志信息的文件
class Log
{
public:
    Log()
    {
        printmethod=SCREEN;//默认的打印方式
        path="./log/";//默认的存放日志的路径
    }
    void enable(int method)
    {
        printmethod=method;//自定义打印方式
    }
    string leveltostring(int level)//将日志等级信息转换成字符串,打印的时候要使用到
    {
        switch(level)
        {
            case INFO:return "INFO:";
            case WARNING:return "WARNING:";
            case ERROR:return "ERROR:";
            case FATAL:return "FATAL:";
            case DEBUG:return "DEBUG:";
            default:return "NONE";
        }
    }
    void operator()(int level,const char* famat,...)
    {
        time_t t=time(nullptr);//获得时间戳
        struct tm*ctime=localtime(&t);//这是获取当地的一个时间,这个结构体李米娜有许多属性。

        char leftbuffer[SIZE];//存放日志时间等信息
        snprintf(leftbuffer,sizeof(leftbuffer),"[%s][%d-%d-%d-%d:%d:%d]",leveltostring(level).c_str(),ctime->tm_year+1990,ctime->tm_mon+1,ctime->tm_mday,ctime->tm_hour,ctime->tm_min,ctime->tm_sec);

        va_list s;
        va_start(s,famat);
        char rightbuffer[SIZE];//存放日志等级信息,有用户去决定的
        vsnprintf(rightbuffer,sizeof(rightbuffer),famat,s);
        va_end(s);

        char logtxt[SIZE*2];//拼在一起
        snprintf(logtxt,sizeof(logtxt),"%s %s",leftbuffer,rightbuffer);


        printfmethod(level,logtxt);//打印方式
    }

    void printfmethod(int level,const string&logtxt)
    {
        switch(printmethod)
        {
            case SCREEN:
                cout<<logtxt<<endl;
            break;
            case ONEFILE:
                printfonefile(LOGTXT,logtxt);//本来一个参数就可以,但是为了适配下面函数的调用,加了一个参数
            break;
            case CLASSFILE:
                printfclassfile(level,logtxt);
            break;
        }
    }

    void printfonefile(const string& logname,const string&logtxt)
    {
        string _logtxt=path+LOGTXT;
        int fd=open(_logtxt.c_str(),O_CREAT|O_WRONLY|O_APPEND);
        if(fd<0)
        {
            perror("open:");
            return;
        }
        write(fd,logtxt.c_str(),logtxt.size());
        close(fd);

    }

    void printfclassfile(int level,const string&logtxt)//多文件打印
    {
        string _logtxt=LOGTXT;
        _logtxt+=".";
        _logtxt+=leveltostring(level);//为了区分不同的日志等职,分别放到不同的文件里面。
        printfonefile(_logtxt,logtxt);
    }
    ~Log()
    {

    }
private:
    int printmethod;//打印方式
    string path;//使日志文件和可执行程序不在一个目录下,单独建立一个目录
};

4.2 介绍知识点

第一个:va_list是什么,这是类型,而va_start这其实是一个读取可变模板参数的起始位置的函数,我给大家据一个例子:任意数相加

int sum(int n,...)
{
    va_list s;
    va_start(s,n);
    int sum=0;

    while(n)
    {
        sum+=va_arg(s,int);
        n--;
    }
    va_end(s);
    return sum;
}

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

所以我们对于这种可变参数在一开始都至少有一个形参。这样大家应该知道作用了吧,我们在写rightbuffer这个字符串的时候还需要一个函数vsnprintf
在这里插入图片描述
这样就很明显看出来这个函数的作用了。

第二个
我们来看获取时间的函数,最重要的localtime·
在这里插入图片描述

这样一看就很清楚了,只是陌生函数多了,但是理解起来和用法都很简单。

4.3 看加入日志的效果

  1. 往屏幕上打印:
    在这里插入图片描述

通过打印出来的日志我们也可以发现,我们的管道只有双方都打开才会打开。这样打出来的消息就很规范

  1. 往一个文件里面打印
    通过log.enable(ONEFILE);去修改打印方式
    在这里插入图片描述

  2. 往多个文件打印,分类

  3. 通过log.enable(CLASSFILE);去修改打印方式
    在这里插入图片描述

通过上面的演示,我们发现我们以后想要打印一些错误信息,就可以来复用这个模板了,让程序看上去非常的优雅。大家下去在好好的理解一下这个模板。

五、总结

今天讲解了命名管道,有了匿名管道的铺垫,我们在理解命名管道的时候就简单许多,所以操作来说也是很简单的,大家下去夺取理解就好了,我们今天讲的重点还有一个就是日志,这个让大家以后都可以重复使用的一个模板非常的方便,大家如果不想去实现,就使用博主写的吧,希望大家下去实现以下,这样更容易理解,而且更有印象。我们下篇讲解的是共享内存的知识,希望大家过啊里支持一下

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mysql数据库【进阶篇】

1.存储引擎 1.1 mysql的体系结构 连接层&#xff1a;最上层是一些客户端和链接服务&#xff0c;主要完成- -些类似于连接处理、授权认证、及相关的安全方案。服务器也会为安全接入的每个客户端验证它所具有的操作权限。服务层&#xff1a;第二层架构主要完成大多数的核心服务功…

【教学类-06-06】20231118 (55格版)X以内加法、减法、加减混合题

背景需求 1、长期做手工制作&#xff0c;常规管理难以控制 优势&#xff1a; 1、幼儿创作热情高涨&#xff0c;发明的新玩具多 2、互助观摩&#xff0c;进一步模仿、创作作品 3、互动游戏兴趣浓厚&#xff0c;语言交流踊跃&#xff0c; 劣势&#xff1a; 1、纸条碎片多&…

深入流行推荐引擎3:Spotify音乐推荐系统

深入流行推荐引擎3&#xff1a;Spotify音乐推荐系统 Spotify音乐推荐系统通过矩阵分解发现每周&#xff08;Discover Weekly via Matrix Factorization&#xff09;Discover Weekly 如何运作&#xff1f;&#xff08;How Discover Weekly Works?&#xff09;矩阵分解&#xff…

【IPC】 共享内存

1、概述 共享内存允许两个或者多个进程共享给定的存储区域。 共享内存的特点 1、 共享内存是进程间共享数据的一种最快的方法。 一个进程向共享的内存区域写入了数据&#xff0c;共享这个内存区域的所有进程就可以立刻看到 其中的内容。 2、使用共享内存要注意的是多个进程…

使用持久卷部署 WordPress 和 MySQL

&#x1f5d3;️实验环境 OS名称Microsoft Windows 11 家庭中文版系统类型x64-based PCDocker版本Docker version 24.0.6, build ed223bcminikube版本v1.32.0 &#x1f587;️创建 kustomization.yaml 你可以通过 kustomization.yaml 中的生成器创建一个 Secret存储密码或密…

Android问题笔记四十六:解决open failed: EACCES (Permission denied) 问题

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列点击跳转>ChatGPT和AIGC &#x1f449;关于作者 专…

Jmeter性能实战之分布式压测

JMeter分布式执行原理 1、JMeter分布式测试时&#xff0c;选择其中一台作为调度机(master)&#xff0c;其它机器作为执行机(slave)。 2、执行时&#xff0c;master会把脚本发送到每台slave上&#xff0c;slave 拿到脚本后就开始执行&#xff0c;slave执行时不需要启动GUI&…

深度学习YOLO抽烟行为检测 - python opencv 计算机竞赛

文章目录 1 前言1 课题背景2 实现效果3 Yolov5算法3.1 简介3.2 相关技术 4 数据集处理及实验5 部分核心代码6 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习YOLO抽烟行为检测 该项目较为新颖&#xff0c;适合作为竞赛课…

Parity Game——种类并查集、权值并查集、离散化

题目描述 思路 怎么得到这个序列中每一段的关系&#xff1f; 我们可以把这个只包含0和1的序列看作一个数组&#xff0c;0表示当前位置为0&#xff0c;1表示当前位置为1&#xff0c;利用前缀和的性质可以知道某一段中所包含的1的数量sum1 a[r] - a[l-1] 如果sum1为偶数&…

Rust开发——切片(slice)类型

1、什么是切片 在 Rust 中&#xff0c;切片&#xff08;slice&#xff09;是一种基本类型和序列类型。在 Rust 官方文档中&#xff0c;切片被定义为“对连续序列的动态大小视图”。 但在rust的Github 源码中切片被定义如下&#xff1a; 切片是对一块内存的视图&#xff0c;表…

如何隐藏Selenium特征实现自动化网页采集

Selenium是一个流行的自动化网页测试工具&#xff0c;可以通过模拟用户在Chrome浏览器中的操作来完成网站的测试。然而&#xff0c;有些网站会检测浏览器是否由Selenium驱动&#xff0c;如果是&#xff0c;就会返回错误的结果或拒绝访问。为了避免这种情况&#xff0c;我们需要…

多线程编程

1 线程的使用 1.1 为什么要使用多线程 在编写代码时&#xff0c;是否会遇到以下的场景会感觉到难以下手&#xff1f; 要做 2 件事&#xff0c;一件需要阻塞等待&#xff0c;另一件需要实时进行。例如播放器&#xff1a;一边在屏幕上播放视频&#xff0c;一边在等待用户的按…

中间件安全:Apache 目录穿透.(CVE-2021-41773)

中间件安全&#xff1a;Apache 目录穿透.&#xff08;CVE-2021-41773&#xff09; Apache 的 2.4.49、2.4.50 版本 对路径规范化所做的更改中存在一个路径穿越漏洞&#xff0c;攻击者可利用该漏洞读取到Web目录外的其他文件&#xff0c;如系统配置文件、网站源码等&#xff0c…

Polygon zkEVM协议治理、升级及其流程

1. 引言 随着Polygon社区开发者和内部团队的测试深入&#xff0c;当前版本的Polygon zkEVM不可避免地需更新和某些升级。 为激励开发者对Polygon zkEVM做battle-test&#xff0c;已启动了bug-bounty&#xff1a; Rewards by Threat Level 由于zk-Rollup生态系统还处于萌芽阶…

算法设计与分析复习--贪心(二)

文章目录 上一篇哈夫曼编码单源最短路最小生成树Kruskal算法Prim算法 多机调度问题下一篇 上一篇 算法设计与分析复习–贪心&#xff08;一&#xff09; 哈夫曼编码 产生这种前缀码的方式称为哈夫曼树 哈夫曼树相关习题AcWing 148. 合并果子 #include <iostream> #inc…

LDO线性稳压器要不要并联二极管?

昨天介绍过了LDO是什么东西&#xff0c;那么对于它的应用场景是怎么的呢&#xff1f;LDO要不要并联二极管呢&#xff1f; 一般来说&#xff0c;LDO是不需要并联二极管的。 看下图第一个是典型电路&#xff0c;第二个是带可调节电压功能的LDO典型电路&#xff0c;从图里就可以…

设计模式-组合模式-笔记

“数据结构”模式 常常有一些组件在内部具有特定的数据结构&#xff0c;如果让客户程序依赖这些特定数据结构&#xff0c;将极大地破坏组件的复用。这时候&#xff0c;将这些特定数据结构封装在内部&#xff0c;在外部提供统一的接口&#xff0c;来实现与特定数据结构无关的访…

一起Talk Android吧(第五百五十四回:分享一个Retorfit使用错误的案例)

文章目录 1. 案例场景2. 案例现象3. 原因分析和解决方案3.1 原因分析3.2 解决方案4. 经验总结各位看官们大家好,上一回中咱们说的例子是"解析Retrofit返回的数据",本章回中将分享一个 Retrofit使用错误的案例。闲话休提,言归正转,让我们一起Talk Android吧! 1. …

三层交换机实现不同VLAN间通讯

默认时&#xff0c;同一个VLAN中的主机才能彼此通信&#xff0c;那么交换机上的VLAN用户之间如何通信&#xff1f; 要实现VLAN之间用户的通信&#xff0c;就必须借助路由器或三层交换机来完成。 下面以三层交换机为例子说明&#xff1a; 注意&#xff1a; 1.交换机与三层交换…

HWS-CTF-第七期山大站-inverse

文章目录 inversemainworkread_intread_n 思路onegadget exp 第一次真正意义上独立在比赛中做出题目来了&#xff0c;距离真正意义接触CTF-PWN差不多正好两个月。但由于不知道靶场要自己开而且端口每次自己打开会改&#xff0c;交flag稍微晚了些&#xff08;我太菜了&#xff0…