Linux系统基础-进程间通信(5)_模拟实现命名管道和共享内存

news2024/11/29 2:31:01

个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创

Linux系统基础-进程间通信(5)_模拟实现命名管道和共享内存

收录于专栏[Linux学习]
本专栏旨在分享学习Linux的一点学习笔记,欢迎大家在评论区交流讨论💌

目录

1. 命名管道的实现

1.1 命名管道类

定义常量与宏

类定义

成员变量 

构造函数

私有方法

公共方法

析构函数

1.2 客户端

1.3 服务端

1.4 效果展示: 

2. 共享内存的实现

2.1 共享内存类

常量定义

成员变量

私有方法

公共方法

2.2 命名管道类

2.3 客户端

2.4 服务端

2.5 效果展示:


1. 命名管道的实现

1.1 命名管道类

这个类 NamePiped 封装了命名管道的基本操作,提供了创建、打开、读取和写入的接口 

#pragma

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

const std::string comm_path = "./myfifo";
#define DefaultFd -1
#define Creater 1
#define User 2
#define Read O_RDONLY
#define Write O_WRONLY
#define BaseSize 4096


class NamePiped
{
private:
    bool OpenNamePipe(int mode)
    {
        _fd = open(_fifo_path.c_str(), mode);
        if(_fd < 0)
            return false;
        return true;
    }

public:
    NamePiped(const std::string &path, int who)
        : _fifo_path(path), _id(who), _fd(DefaultFd)
    {
        if(_id == Creater)
        {
            int res = mkfifo(_fifo_path.c_str(), 0666);
            if(res != 0)
            {
                perror("mkfifo");
            }
            std::cout << "create create named pipe" << std::endl;
        }
    }
    bool OpenForRead()
    {
        return OpenNamePipe(Read);
    }
    bool OpenForWrite()
    {
        return OpenNamePipe(Write);
    }

    int ReadNamePipe(std::string *out)
    {
        char buffer[BaseSize];
        int n = read(_fd, buffer, sizeof(buffer));
        if(n > 0)
        {
            buffer[n] = 0;
            *out = buffer;
        }
        return n;
    }

    int WriteNamePipe(const std::string &in)
    {
        return write(_fd, in.c_str(), in.size());
    }

    ~NamePiped()
    {
        if(_id == Creater)
        {
            int res = unlink(_fifo_path.c_str());
            if(res != 0)
            {
                perror("unlink");
            }
            std::cout << "creater free named pipe" << std::endl;
        }
        if(_fd != DefaultFd) close(_fd);
    }
private:
    const std::string _fifo_path;
    int _id;
    int _fd;
};

定义常量与宏

const std::string comm_path = "./myfifo";: 定义了一个常量字符串,表示命名管道的路径。

#define DefaultFd -1: 定义一个默认的文件描述符值,表示未打开状态。

#define Creater 1: 定义服务端标识,用于区分管道的服务端和客户端

#define User 2: 定义客服端标识,用于标识读取或写入管道的用户。

#define Read O_RDONLY 和 #define Write O_WRONLY: 分别定义读取和写入的打开模式。

#define BaseSize 4096: 定义缓冲区的大小,用于读取数据。

类定义

成员变量 

const std::string _fifo_path;: 保存命名管道的路径。

int _id;: 标识是服务端还是用户。

int _fd;: 文件描述符,用于读写操作。

构造函数

初始化成员变量: 使用初始化列表设置路径、身份和默认文件描述符。

创建管道: 如果 _id 是 Creater(服务端),则调用 mkfifo 创建命名管道。如果创建失败,使用 perror 输出错误信息。

私有方法

OpenNamePipe(int mode) : 功能: 封装打开命名管道的逻辑。

调用 open 函数尝试打开管道。

如果打开失败,返回 false,成功则返回 true。

公共方法

OpenForRead() 功能: 调用 OpenNamePipe 以只读模式打开管道。

OpenForWrite() 功能: 调用 OpenNamePipe 以只写模式打开管道。

ReadNamePipe(std::string *out) 功能: 从管道读取数据。

定义一个缓冲区,调用 read 从文件描述符读取数据。

如果读取成功,将数据存入输出参数,并返回读取的字节数。

WriteNamePipe(const std::string &in) 功能: 向管道写入字符串数据。

析构函数

~NamePiped() 功能: 释放资源和清理工作。

如果 _id 是 Creater,则使用 unlink 删除命名管道。

关闭文件描述符(如果已打开)。

1.2 客户端

#include "namedPipe.hpp"

//write
int main()
{
    NamePiped fifo(comm_path, User);
    if(fifo.OpenForWrite())
    {
        std::cout << "client open named pipe done" << std::endl;
        while(true)
        {
            std::cout << "Please Enter > ";
            std::string message;
            std::getline(std::cin, message);
            fifo.WriteNamePipe(message);
        }
    }
    return 0;
}

1.3 服务端

#include "namedPipe.hpp"
//server read : 管理命名管道的整个生命周期

int main()
{
    NamePiped fifo(comm_path, Creater);
    //对于进程而言, 如果我们打开文件, 但是写还没来, 进程会阻塞在open调用中, 直到对方打开
    //进程同步
    if(fifo.OpenForRead())
    {
        std::cout << "server open named pipe done" << std::endl;
        sleep(3);
        while(true)
        {
            std::string message;
            int n = fifo.ReadNamePipe(&message);
            if(n > 0)
            {
                std::cout << "Client Say > " << message << std::endl;
            }
            else if(n == 0)
            {
                std::cout << "Client quit, Server Too!" << std::endl;
            }
            else
            {
                std::cout << "fifo.ReadNamedPipe Error" << std::endl;
                break;
            }
        }
    }
    return 0;
}

1.4 效果展示: 

通过命名管道, 我们完成了客户端与服务器端的通信! (因为命名管道不需要两个进程具有血缘关系) 

2. 共享内存的实现

完成上述客户端与服务器端的通信, 除了使用命名管道, 共享内存同样可以!

2.1 共享内存类

#include <iostream>
#include <string>
#include <cerrno>
#include <cstdio>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
 #include <string.h>

const int gCreater = 1;
const int gUser = 2;
const std::string gpathname = "/home/cjb/linux-operating-system-network/linux22/shm";
const int gproj_id = 0x66;
const int gShmSize = 4097; // 4096*n

class Shm
{
private:
    key_t GetCommKey()
    {
        key_t k = ftok(_pathname.c_str(), _proj_id);
        if (k < 0)
        {
            perror("ftok");
        }
        return k;
    }
    int GetShmHelper(key_t key, int size, int flag)
    {
        int shmid = shmget(key, size, flag);
        if (shmid < 0)
        {
            perror("shmget");
        }
        return shmid;
    }

    std::string RoleToString(int who)
    {
        if (who == gCreater)
            return "Creater";
        else if (who == gUser)
            return "gUser";
        else
            return "None";
    }
    void *AttachShm()
    {
        if (_addrshm != nullptr)
            DetachShm(_addrshm);
        void *shmaddr = shmat(_shmid, nullptr, 0);
        if (shmaddr == nullptr)
        {
            perror("shmat");
        }
        std::cout << "who: " << RoleToString(_who) << "attach shm..." << std::endl;
        return shmaddr;
    }

    void DetachShm(void *shmadder)
    {
        if (shmadder == nullptr)
            return;
        shmdt(shmadder);
        std::cout << "who: " << RoleToString(_who) << "detach shm..." << std::endl;
    }

public:
    Shm(const std::string &pathname, int proj_id, int who)
        : _pathname(pathname), _proj_id(proj_id), _who(who), _addrshm(nullptr)
    {
        _key = GetCommKey();
        if (_who == gCreater)
            GetShmUseCreate();
        else if (_who == gUser)
            GetShmForUse();
        _addrshm = AttachShm();

        std::cout << "shmid: " << _shmid << std::endl;
        std::cout << "_key: " << ToHex(_key) << std::endl;
    }

    ~Shm()
    {
        if (_who == gCreater)
        {
            int res = shmctl(_shmid, IPC_RMID, nullptr);
        }
        std::cout << "shm remove done..." << std::endl;
    }

    std::string ToHex(key_t key)
    {
        char buffer[128];
        snprintf(buffer, sizeof(buffer), "0x%x", key);
        return buffer;
    }

    bool GetShmUseCreate()
    {
        if (_who == gCreater)
        {
            _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | IPC_EXCL | 0666);
            if (_shmid >= 0)
                return true;
            std::cout << "shm create done..." << std::endl;
        }
        return false;
    }

    bool GetShmForUse()
    {
        if (_who == gUser)
        {
            _shmid = GetShmHelper(_key, gShmSize, IPC_CREAT | 0666);
            if (_shmid >= 0)
                return true;
            std::cout << "shm get done..." << std::endl;
        }
        return false;
    }

    void Zero()
    {
        if (_addrshm)
        {
            memset(_addrshm, 0, gShmSize);
        }
    }

    void *Addr()
    {
        return _addrshm;
    }

    void DebugShm()
    {
        struct shmid_ds ds;
        int n = shmctl(_shmid, IPC_STAT, &ds);
        if (n < 0)
            return;
        std::cout << "ds.shm_prem.__key : " << ToHex(ds.shm_perm.__key) << std::endl;
        std::cout << "ds.shm_nattch" << ds.shm_nattch << std::endl;
    }

private:
    key_t _key;
    int _shmid;

    std::string _pathname;
    int _proj_id;

    int _who;
    void *_addrshm;
};

常量定义

gCreater gUser:分别表示创建者和用户角色的常量,用于区分不同的操作角色。

gpathname:共享内存文件路径,ftok 使用此路径生成唯一的键。

gproj_id:用于生成唯一键的项目 ID。

gShmSize:共享内存的大小,设置为 4097 字节(必须是 4096 的倍数)。

成员变量

_key:存储生成的共享内存键。

_shmid:共享内存 ID。

_pathname:存储共享内存路径。

_proj_id:存储项目 ID。

_who:当前对象的角色(创建者或用户)。

_addrshm:指向当前附加的共享内存的指针。

私有方法

GetCommKey():

使用 ftok 函数生成共享内存的键。如果生成失败,打印错误信息。

GetShmHelper():

封装对 shmget 的调用,尝试获取共享内存 ID。如果失败,打印错误信息。

RoleToString():

将角色 ID 转换为字符串,方便调试输出。

AttachShm():

尝试附加共享内存。如果 _addrshm 不为空,先调用 DetachShm() 分离它。使用 shmat 函数附加共享内存,并打印调试信息。

DetachShm():

封装对 shmdt 的调用,用于分离共享内存。如果传入的指针为空,直接返回。

公共方法

构造函数:

接受路径、项目 ID 和角色,并初始化相关变量。

调用 GetCommKey() 获取键。

根据角色调用 GetShmUseCreate() 或 GetShmForUse() 来获取共享内存。

调用 AttachShm() 附加共享内存,打印共享内存 ID 和键。

析构函数:

如果角色是创建者,调用 shmctl 删除共享内存(IPC_RMID)。

ToHex():

将共享内存键转换为十六进制字符串,方便输出调试信息。

GetShmUseCreate():

仅在创建者角色时调用,尝试创建共享内存段(IPC_CREAT | IPC_EXCL),成功返回 true。

GetShmForUse():

仅在用户角色时调用,尝试获取共享内存段(IPC_CREAT),成功返回 true。

Zero():

清零共享内存内容,使用 memset。

Addr():

返回当前附加的共享内存地址。

DebugShm():

获取共享内存的状态信息(如键和连接数),使用 shmctl 和 IPC_STAT。

2.2 命名管道类

和上面一样, 不需要改变

#pragma

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

const std::string comm_path = "./myfifo";
#define DefaultFd -1
#define Creater 1
#define User 2
#define Read O_RDONLY
#define Write O_WRONLY
#define BaseSize 4096

// const std::string comm_path = "./myfifo";: 定义了一个常量字符串,表示命名管道的路径。
// #define DefaultFd -1: 定义一个默认的文件描述符值,表示未打开状态。
// #define Creater 1: 定义创建者标识,用于区分管道的创建者和用户。
// #define User 2: 定义用户标识,用于标识读取或写入管道的用户。
// #define Read O_RDONLY 和 #define Write O_WRONLY: 分别定义读取和写入的打开模式。
// #define BaseSize 4096: 定义缓冲区的大小,用于读取数据。

// 这个类 NamePiped 封装了命名管道的基本操作,提供了创建、打开、读取和写入的接口
class NamePiped
{
private:
    bool OpenNamePipe(int mode)
    {
        _fd = open(_fifo_path.c_str(), mode);
        if(_fd < 0)
            return false;
        return true;
    }

public:
    NamePiped(const std::string &path, int who)
        : _fifo_path(path), _id(who), _fd(DefaultFd)
    {
        if(_id == Creater)
        {
            int res = mkfifo(_fifo_path.c_str(), 0666);
            if(res != 0)
            {
                perror("mkfifo");
            }
            std::cout << "create create named pipe" << std::endl;
        }
    }
    bool OpenForRead()
    {
        return OpenNamePipe(Read);
    }
    bool OpenForWrite()
    {
        return OpenNamePipe(Write);
    }

    int ReadNamePipe(std::string *out)
    {
        char buffer[BaseSize];
        int n = read(_fd, buffer, sizeof(buffer));
        if(n > 0)
        {
            buffer[n] = 0;
            *out = buffer;
        }
        return n;
    }

    int WriteNamedPipe(const std::string &in)
    {
        return write(_fd, in.c_str(), in.size());
    }

    ~NamePiped()
    {
        if(_id == Creater)
        {
            int res = unlink(_fifo_path.c_str());
            if(res != 0)
            {
                perror("unlink");
            }
            std::cout << "creater free named pipe" << std::endl;
        }
        if(_fd != DefaultFd) close(_fd);
    }
private:
    const std::string _fifo_path;
    int _id;
    int _fd;
};

2.3 客户端

#include "Shm.hpp"
#include "namedPipe.hpp"

int main()
{
    // 1. 创建共享内存

    Shm shm(gpathname, gproj_id, gUser);
    shm.Zero();
    char *shmaddr = (char *)shm.Addr();
    sleep(3);

    // 2. 打开管道
    NamePiped fifo(comm_path, User);
    fifo.OpenForWrite();

    // 当成string
    char ch = 'A';
    while (ch <= 'Z')
    {
        shmaddr[ch - 'A'] = ch;

        std::string temp = "wakeup";
        std::cout << "add " << ch << " into Shm, " << "wakeup reader" << std::endl;
        fifo.WriteNamedPipe(temp);
        sleep(2);
        ch++;
    }
    return 0;
}

2.4 服务端

#include "Shm.hpp"
#include "namedPipe.hpp"

int main()
{
    // 1. 创建共享内存
    Shm shm(gpathname, gproj_id, gCreater);
    char *shmaddr = (char*)shm.Addr();

    shm.DebugShm();

    sleep(5);
    
    return 0;
}

2.5 效果展示:

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

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

相关文章

LeetCode--删除并获得点数--动态规划

一、题目解析 二、算法原理 根据题意&#xff0c;在选择了元素 x 后&#xff0c;该元素以及所有等于 x−1 或 x1 的元素会从数组中删去。若还有多个值为 x 的元素&#xff0c;由于所有等于 x−1 或 x1 的元素已经被删除&#xff0c;我们可以直接删除 x 并获得其点数。因此若选…

Win10+MinGW13.1.0编译Qt5.15.15

安装windows SDK、python、ruby、cmake、Perl[可选]安装MySQL解压qt-everywhere-opensource-src-5.15.15.zip&#xff08;注&#xff1a;不要使用qt-everywhere-opensource-src-5.15.15.tar.xz&#xff09;修改源代码 E:\qt-everywhere-src-5.15.15\qtbase\src\3rdparty\angle\…

hive数据库,表操作

1.创建; create database if not exists myhive; use myhive; 2.查看: 查看数据库详细信息:desc database myhive; 默认数据库的存放路径是 HDFS 的&#xff1a; /user/hive/warehouse 内 补充:创建数据库并指定 hdfs 存储位置:create database myhive2 location /myhive2 3.…

<项目代码>YOLOv8路面垃圾识别<目标检测>

YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一个回归问题&#xff0c;能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法&#xff08;如Faster R-CNN&#xff09;&#xff0c;YOLOv8具有更高的…

Codeforces Round 881 (Div. 3)(A~F1题解)

这场比赛可能是因为比较老吧&#xff0c;我感觉很轻松&#xff0c;就是第五个卡了一下&#xff0c;看错题了&#xff0c;原本应该是严格大于的&#xff0c;从而导致代码一直出现bug&#xff0c;在1小时20分钟才解决 A. Sasha and Array Coloring 题意&#xff1a;就是说给你n个…

提权 | Windows系统

文章目录 cmd提权meterpreter提权getsystemsteal_tokenmigrate 令牌窃取(MS16-075)烂土豆提权步骤烂土豆提权原理 sc命令提权抓本地密码提权其他工具pr工具 内核提权WindowsVulScan cmd提权 前言&#xff1a;我们getshell一个用windows部署的网站后&#xff0c;通过蚁剑或者其…

ESP32 S3 语音识别 语音唤醒程序流程

ESP32 S3 语音识别 语音唤醒程序流程 参考例程首先进行esp_periph_set_init 初始化之后执行setup_player&#xff0c;之后执行start_recorder&#xff0c;识别的主处理voice_read_task 参考例程 D:\Espressif\esp-adf\examples\speech_recognition\wwe\ 首先进行esp_periph_se…

零知识学习WLAN漫游二、无线漫游介绍(2)

接前一篇文章&#xff1a;零知识学习WLAN漫游一、无线漫游介绍&#xff08;1&#xff09; 本文内容参考&#xff1a; WLAN漫游简介_漫游主动性-CSDN博客 无线漫游_百度百科 无线漫游简述-CSDN博客 特此致谢&#xff01; 一、WLAN漫游简介 3. 漫游协议和快速漫游协议 802.…

算法的学习笔记—数字在排序数组中出现的次数(牛客JZ53)

&#x1f600;前言 在编程中&#xff0c;查找有序数组中特定元素的出现次数是一个常见的问题。本文将详细讲解这个问题的解决方案&#xff0c;并通过二分查找法优化效率。 &#x1f3e0;个人主页&#xff1a;尘觉主页 文章目录 &#x1f970;数字在排序数组中出现的次数&#x…

九、pico+Unity交互开发——触碰抓取

一、VR交互的类型 Hover&#xff08;悬停&#xff09; 定义&#xff1a;发起交互的对象停留在可交互对象的交互区域。例如&#xff0c;当手触摸到物品表面&#xff08;可交互区域&#xff09;时&#xff0c;视为触发了Hover。 Grab&#xff08;抓取&#xff09; 概念&#xff…

深入浅出:深度学习模型部署全流程详解

博主简介&#xff1a;努力学习的22级计算机科学与技术本科生一枚&#x1f338;博主主页&#xff1a; Yaoyao2024往期回顾&#xff1a; 【论文精读】PSAD&#xff1a;小样本部件分割揭示工业异常检测的合成逻辑每日一言&#x1f33c;: 生活要有所期待&#xff0c; 否则就如同罩在…

【国潮来袭】华为原生鸿蒙 HarmonyOS NEXT(5.0)正式发布:鸿蒙诞生以来最大升级,碰一碰、小艺圈选重磅上线

在昨日晚间的原生鸿蒙之夜暨华为全场景新品发布会上&#xff0c;华为原生鸿蒙 HarmonyOS NEXT&#xff08;5.0&#xff09;正式发布。 华为官方透露&#xff0c;截至目前&#xff0c;鸿蒙操作系统在中国市场份额占据 Top2 的领先地位&#xff0c;拥有超过 1.1 亿 的代码行和 6…

想让前后端交互更轻松?alovajs了解一下?

作为一个前端开发者&#xff0c;我最近发现了一个超赞的请求库 alovajs&#xff0c;它真的让我眼前一亮&#xff01;说实话&#xff0c;我感觉自己找到了前端开发的新大陆。大家知道&#xff0c;在前端开发中&#xff0c;处理 Client-Server 交互一直是个老大难的问题&#xff…

查缺补漏----用户工作区,系统缓冲区,外设工作最短时间计算

对于下面这一题&#xff0c;分析起来很简单&#xff1a; 答案&#xff1a;C 以上是单缓冲区&#xff0c;补充双缓冲区是什么情况&#xff1a; 1.假设磁盘块与缓冲区大小相同&#xff0c;每个盘块读入缓冲区的时间为15us&#xff0c;由缓冲区送至用户区的时间是5us&#xff0c…

etl-查询错误log日志和oracle删除数据表空间

查看weblogic日志的目录 建立连接ssh root192.168.30.1xx 密码hygd123 找到下面路径中的文件 cd /home/weblogic/Oracle/Middleware/user_projects/domains/base_domain/bapp-logs 查看log日志 tail -f -n 400 Adminservers.log oracle删除表空间&#xff08;切换到dba用户…

Android 13 SystemUI 隐藏下拉快捷面板部分模块(wifi,bt,nfc等)入口

frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java createTileInternal(tileSpec)方法注释想隐藏的模块即可。

Qt开发-----线程调度

目录 前言 一、Linux下查看进程的情况 二、线程的创建 三、多线程的创建和使用 前言 以下引用内容源自正点原子Qt开发指南文档。 我们写的一个应用程序&#xff0c;应用程序跑起来后一般情况下只有一个线程&#xff0c;但是可能也有特殊情况。比如我们前面章节写的例程都跑…

《YOLO目标检测》—— YOLOv1 详细介绍

文章目录 一、算法特点二、网络结构三、检测过程四、损失函数五、性能表现六、优缺点 YOLO v1&#xff08;You Only Look Once version 1&#xff09;是一种快速的目标检测算法&#xff0c;以下是对YOLO v1的详细介绍&#xff1a; 一、算法特点 端到端的网络结构&#xff1a;Y…

项目:Boost 搜索引擎

项目&#xff1a;Boost 搜索引擎 1、项目背景 公司&#xff1a;百度、360、搜狗、谷歌 …站内搜索&#xff1a;搜索的数据更垂直&#xff08;相关&#xff09;&#xff0c;数据量小 2、整体框架 3、技术栈和项目环境 技术栈&#xff1a;C/C C11&#xff0c;STL&#xff0c;jso…

【JAVA毕设】基于JAVA的仓库管理系统

一、项目介绍 本系统前端框架采用了比较流行的渐进式JavaScript框架Vue.js。使用Vue-Router实现动态路由&#xff0c;Ajax实现前后端通信&#xff0c;Element-plus组件库使页面快速成型。后端部分&#xff1a;采用SpringBoot作为开发框架&#xff0c;同时集成MyBatis、Redis、…