Linux -- 共享内存(2)

目录

命令 ipcs -m :

命令 ipcrm -m shmid:

 共享内存的通信:

为什么共享内存更高效?

代码:

ShmClient.cc:

ShmServer.cc:

结果:

如何让共享内存实现同步?

代码:

Comm.hpp

Fifo.hpp

ShmClient.cc 

 ShmServer.cc

结果:


接上篇的共享内存的函数封装。

当两个不同的进程 server 和 client 的 GetShmKeyOrDie 函数传了一样的 pathname 和 pro_jid 时,ftok 就会给两个不同的进程返回一样的 key 值!即两个不同的进程看到同一个共享内存!

命令 ipcs -m :

命令 ipcs -m 可以查看共享内存的相关信息,如下图:

perms 是共享内存的权限。

  • 当我们调用 GetShm 函数时,由于没有设置权限,故创建出来的共享内存的权限为 0(如上图);
  • 当我们调用 CreateShm 函数时,由于设置的权限为 0666,故创建出来的共享内存的权限为 666(如下图)。

 

bytes 是共享内存的大小,单位为字节,共享内存的大小是用户定义的。 

nattch 是共享内存挂接的进程的个数

我们可以写个代码验证一下:我们创建出共享内存后休眠 2s,然后把共享内存挂接到进程中,再次休眠 5s,休眠结束后把共享内存从进程的地址空间中分离出来,注意只是分离出来,并没有删除共享内存。

#include"Comm.hpp"
#include<unistd.h>
int main()
{
    key_t k=GetShmKeyOrDie(path,pro_jid);
    cout<<" key:"<<ToHex(k)<<endl;//转为十六进制输出

    int shmid=CreateShm(k,defaultsize);
    cout<<" shmid:"<<shmid<<endl;
    sleep(2);

    char* addr=(char*)ShmAttach(shmid);
    cout<<" Attach success, addr:"<<ToHex((uint64_t)addr)<<endl;

    sleep(5);

    ShmDetach(addr);
    cout<<" Detach success "<<ToHex((uint64_t)addr)<<endl;

    return 0;
}

 用 while :;do ipcs -m;sleep 1;done 命令时时监控共享内存的信息:

命令 ipcrm -m shmid:

除了调用函数删除,还可以用命令 ipcrm -m shmid 来删除共享内存,如下图,删除完再次查看共享内存的信息时,已经查不到了:

 共享内存的通信:

创建出共享内存之后,我们就可以通信了。

共享内存可以直接写入,不需要像管道一样调用系统调用,因为它本质上是一块内存区域,这块内存区域由操作系统映射到多个进程的地址空间中。当一个进程修改了这块共享内存中的数据时,其他进程可以立即看到这些修改,因为它们共享的是同一块物理内存。这提高了通信的效率。

为什么共享内存更高效?

共享内存之所以被认为是一种高效的进程间通信(IPC)方式,主要是因为它避免了数据复制和频繁的系统调用。以下是具体原因:

  • 减少数据拷贝:在使用共享内存时,数据只需要在一个地方修改即可被所有有权访问该内存段的进程看到。相比之下,其他IPC机制如管道(pipe)等通常需要将数据从发送者的用户空间复制到内核空间,然后再从内核空间复制到接收者的用户空间。这种多次的数据拷贝过程会消耗额外的时间和CPU资源。
  • 直接内存访问:共享内存使得进程可以直接对内存进行读写操作,就像操作自己的私有内存一样。这种直接访问的方式减少了中间环节,提高了数据传输的速度。

代码:

ShmClient.cc:

#include"Comm.hpp"
#include<unistd.h>

int main()
{
    key_t k=GetShmKeyOrDie(path,pro_jid);
    cout<<" key:"<<ToHex(k)<<endl;//转为十六进制输出
  

    int shmid=GetShm(k,defaultsize);
    cout<<" shmid:"<<shmid<<endl;
 

    char* addr=(char*)ShmAttach(shmid);
    cout<<" Attach success, addr:"<<ToHex((uint64_t)addr)<<endl;


    memset(addr,0,defaultsize);//初始化共享内存

    for(char ch='A';ch<='Z';ch++)//写入
    {
        addr[ch-'A']=ch;
        sleep(1);
    }

    ShmDetach(addr);
    cout<<" Detach success "<<ToHex((uint64_t)addr)<<endl;
    return 0;
}

ShmServer.cc:

#include"Comm.hpp"
#include<unistd.h>
int main()
{
    key_t k=GetShmKeyOrDie(path,pro_jid);
    cout<<" key:"<<ToHex(k)<<endl;//转为十六进制输出

    int shmid=CreateShm(k,defaultsize);
    cout<<" shmid:"<<shmid<<endl;
 
    char* addr=(char*)ShmAttach(shmid);
    cout<<" Attach success, addr:"<<ToHex((uint64_t)addr)<<endl;

    //读取数据
    for(;;)
    {
        cout<<" shm content:"<<addr<<endl;
        sleep(1);
    }

    ShmDetach(addr);
    cout<<" Detach success "<<ToHex((uint64_t)addr)<<endl;

    DeleteShm(shmid);
    return 0;
}

结果:

可以看出,写端 client 还没有向共享内存写入数据,但读端 server 已经在读取了,即读端没有阻塞等待写端写入数据!也就是说共享内存没有提供协同机制!这将导致数据不一致!

如何让共享内存实现同步?

我们利用管道来实现同步。

我们并不是将要写入共享内存的数据写入管道中,而是设置一个管道,server 使用管道的读端, client 使用管道的写端。

共享内存的读端 server 因为管道中还没有写入数据,就阻塞等待,等待管道中的数据,等到了管道的数据才可以读取共享内存的数据,而共享内存的写端 client 写完数据后,向管道写入一个数据,server 的管道读端读到了这个数据,结束阻塞,server就可以读取共享内存的数据了。

代码:

Comm.hpp

#pragma once
#include <iostream>
#include <sys/ipc.h>
#include <cstdlib>
#include <cerrno>
#include <string>
#include <cstring>
#include <sys/shm.h>
#include <sys/types.h>
using namespace std;

const char *path = "./shm_test";
const int pro_jid = 0x66;
const int defaultsize = 4096;

key_t GetShmKeyOrDie(const char *pathname, const int pro_jid)
{
    key_t k = ftok(pathname, pro_jid); // 得到key值
    if (k < 0)// 获取失败
    {
        cerr << " ftok failed,errno:" << errno << ", errstring:" << strerror(errno) << endl;
        exit(1); // 直接终止程序
    }
    return k; // 获取成功
}

int CreateShmOrDie(key_t key, int size, int flag)
{
    // 得到共享内存的shmid
    int shmid = shmget(key, size, flag);
    if (shmid < 0) // 创建失败
    {
        cerr << " shmget failed, errno:" << errno << ", errstring:" << strerror(errno) << endl;
        exit(2);
    }
    return shmid; // 创建成功
}

int CreateShm(key_t key, int size)
{
    // 创建一个新的共享内存,并设置权限
    return CreateShmOrDie(key, size, IPC_CREAT | IPC_EXCL | 0666);
}

int GetShm(key_t key, int size)
{
    return CreateShmOrDie(key, size, IPC_CREAT);
}

void DeleteShm(int shmid)
{
    int n = shmctl(shmid, IPC_RMID, nullptr);
    if (n < 0)
        cerr << " Delete failed,errno:" << errno << ", errstring:" << strerror(errno) << endl;
    else
        cout << " Delete success, shmid:" << shmid << endl;
}

string ToHex(key_t k)//将 key 值转为十六进制
{
    char buffer[1024];//用C语言方便用 %x 直接转为十六进制
    snprintf(buffer,sizeof(buffer),"0x%x",k);
    return buffer;
}

void DebugShm(int shmid)
{
    struct shmid_ds shmds;
    int n = shmctl(shmid, IPC_STAT, &shmds);
    if (n < 0)
        cerr << " shmctl failed " << endl;
    else
    {
        std::cout << "shmds.shm_segsz: " << shmds.shm_segsz << std::endl;//共享内存的大小
        std::cout << "shmds.shm_nattch:" << shmds.shm_nattch << std::endl;//有多少个进程挂接
        std::cout << "shmds.shm_ctime:" << shmds.shm_ctime << std::endl;//上一次挂接或取消挂接的时间
        std::cout << "shmds.shm_perm.__key:" << ToHex(shmds.shm_perm.__key) << std::endl;//对应的key
    }
}

void *ShmAttach(int shmid)
{
    void* addr=shmat(shmid,nullptr,0);
    if((long long int)addr==-1)
    {
        //挂接失败
        cerr<<" attach failed,errno:"<<errno<<", errstring:"<<strerror(errno)<<endl;
        return nullptr;
    }
    else
    {
        //挂接成功
        return addr;
    }
}

void ShmDetach(void *addr)
{
    int n=shmdt(addr);
    if(n<0)
    {
        cerr<<" Detach failed,errno:"<<errno<<", errstring:"<<strerror(errno)<<endl;
    }
}

Fifo.hpp

#ifndef __COMM_HPP__
#define __COMM_HPP__

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

using namespace std;

#define Mode 0666
#define Path "./fifo"

class Fifo
{
public:
    Fifo(const string &path = Path) : _path(path)
    {
        umask(0);
        int n = mkfifo(_path.c_str(), Mode);
        if (n == 0)
        {
            cout << "mkfifo success" << endl;
        }
        else
        {
            cerr << "mkfifo failed, errno: " << errno << ", errstring: " << strerror(errno) << endl;
        }
    }
    ~Fifo()
    {
        int n = unlink(_path.c_str());
        if (n == 0)
        {
            cout << "remove fifo file " << _path << " success" << endl;
        }
        else
        {
            cerr << "remove failed, errno: " << errno << ", errstring: " << strerror(errno) << endl;
        }
    }

private:
    string _path; // 文件路径+文件名
};

class Sync
{
public:
    Sync():rfd(-1),wfd(-1)
    { }

    void OpenReadOrDie()
    {
        rfd=open(Path,O_RDONLY);
        if(rfd<0)
            exit(1);
    }
    void OpenWriteOrDie()
    {
        wfd=open(Path,O_WRONLY);
        if(wfd<0)
            exit(1);
    }

    bool Wait()
    {
        //读端
        bool ret=true;
        uint32_t c=0;
        ssize_t n=read(rfd,&c,sizeof(uint32_t));
        if(n==sizeof(uint32_t))
        {
            cout<<" server wakeup, begin read shm..." << endl;
        }
        else if(n==0)
        {
            return false;
        }
        else
        {
            return false;
        }
        return true;
    }

    void Wakeup()
    {
        //写端
        uint32_t c=0;
        ssize_t n=write(wfd,&c,sizeof(c));
        assert(n==sizeof(uint32_t));
        cout<<" Wakeup server "<<endl;
    }

    ~Sync()
    { }
private:
    int rfd;
    int wfd;
};

#endif

ShmClient.cc 

#include"Comm.hpp"
#include"Fifo.hpp"
#include<unistd.h>

int main()
{
    key_t k=GetShmKeyOrDie(path,pro_jid);
    cout<<" key:"<<ToHex(k)<<endl;//转为十六进制输出
  

    int shmid=GetShm(k,defaultsize);
    cout<<" shmid:"<<shmid<<endl;
 

    char* addr=(char*)ShmAttach(shmid);
    cout<<" Attach success, addr:"<<ToHex((uint64_t)addr)<<endl;

    Sync sync;
    sync.OpenWriteOrDie();//打开写端

    memset(addr,0,defaultsize);//初始化共享内存

    sleep(5);
    for(char ch='A';ch<='Z';ch++)//写入
    {
        addr[ch-'A']=ch;
        sleep(1);
        sync.Wakeup();//写完了
    }

    ShmDetach(addr);
    cout<<" Detach success "<<ToHex((uint64_t)addr)<<endl;
    return 0;
}

 ShmServer.cc

#include"Comm.hpp"
#include"Fifo.hpp"
#include<unistd.h>
int main()
{
    key_t k=GetShmKeyOrDie(path,pro_jid);
    cout<<" key:"<<ToHex(k)<<endl;//转为十六进制输出

    int shmid=CreateShm(k,defaultsize);
    cout<<" shmid:"<<shmid<<endl;
 
    char* addr=(char*)ShmAttach(shmid);
    cout<<" Attach success, addr:"<<ToHex((uint64_t)addr)<<endl;

    Fifo fifo;
    Sync sync;
    sync.OpenReadOrDie();//打开读端

    //读取数据
    for(;;)
    {
        if(!sync.Wait())  break;
        
        //写端写完了,读端可以读了
        cout<<" shm content:"<<addr<<endl;
        sleep(1);
    }

    ShmDetach(addr);
    cout<<" Detach success "<<ToHex((uint64_t)addr)<<endl;

    DeleteShm(shmid);
    return 0;
}

结果:

server 运行,并没有向之前一样直接读数据,而是等待写端写入数据:

 client 写入数据后,读端才开始读数据:

 

 

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

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

相关文章

119.WEB渗透测试-信息收集-ARL(10)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;118.WEB渗透测试-信息收集-ARL&#xff08;9&#xff09; 释放完成后&#xff0c;点击创…

[mysql]子查询的概述和分类及单行子查询

子查询引入 查询的基本结构已经给大家了,子查询里面也是有一些新的内容,子查询其实就是在查询中嵌套另一个查询,叫嵌套查询可能大家更容易理解一点..,类似与FOR循环和FOR循环的嵌套,这一章是我们查询的最难的部分,大家 难度是查询的顶峰,多表查询和子查询是非常重要,SQL优化里…

2024年AR游戏市场分析:创业指南

自从2016年《Pokmon GO》横空出世以来,增强现实(AR)游戏已经成为移动游戏领域的一大亮点。然而,随着时间的推移,AR游戏市场是否仍然充满机遇?本文将对当前市场上成功的AR游戏进行分析,并为有意进入这一领域的创业者提供一些启示。 一、市场领导者分析 《Pokmon GO》自…

kotlin实现viewpager

说明:kotlin tablayout viewpager adapter实现滑动界面 效果图 step1: package com.example.flushfragmentdemoimport androidx.appcompat.app.AppCompatActivity import android.os.Bundle import androidx.fragment.app.Fragment import androidx.viewpager2.adapter.…

用哪种建站程序做谷歌SEO更容易?

做网站很容易&#xff0c;但做一个能带来流量和订单的网站就没那么简单了。尤其是在谷歌SEO优化方面&#xff0c;不同的建站程序对SEO的支持程度也不同。在这方面&#xff0c;WordPress和Shopify无疑是最佳选择。 WordPress作为一个内容管理系统&#xff08;CMS&#xff09;&am…

【无人机设计与控制】基于Astar算法无人机路径规划,优化路径平滑

摘要 本文提出了一种基于A算法的无人机路径规划方法&#xff0c;并通过路径平滑优化提升路径的可行性和安全性。传统A算法在生成路径时&#xff0c;常因路径节点分布不规则导致路径不平滑&#xff0c;影响无人机的飞行效率和安全性。本文通过引入贝塞尔曲线对A*算法生成的路径…

【C++笔记】模板初阶

前言 各位读者朋友们大家好&#xff0c;上期我们讲完了C的内存管理部分&#xff0c;这一期我们开始初步认识一下模板。 目录 前言一. 泛型编程二. 函数模板2.1 函数模板概念2.2 函数模板的格式2.3 函数模板的原理2.4 函数模板的实例化2.5 模板函数匹配规则 三. 类模板3.1 类模…

vue3组件通信--props

目录 1.父传子2.子传父 最近在做项目的过程中发现&#xff0c;props父子通信忘的差不多了。下面写个笔记复习一下。 1.父传子 父组件&#xff08;FatherComponent.vue&#xff09;&#xff1a; <script setup> import ChildComponent from "/components/ChildComp…

海外媒体发稿:如何打造媒体发稿策略

新闻媒体的发稿推广策略对于提升品牌知名度、吸引流量以及增加收入非常重要。本文将介绍一套在21天内打造爆款新闻媒体发稿推广策略的方法。 第一天至第七天&#xff1a;明确目标和定位 在这个阶段&#xff0c;你需要明确你的目标和定位&#xff0c;以便为你的新闻媒体建立一个…

1U服务器和Hyper-V虚拟机使用记录

记录最近接触服务器和虚拟机的一些使用操作知识 背景&#xff1a;1U服务器上架使用&#xff0c;备份其他服务器vm虚拟机&#xff0c;Hyper-V管理虚拟机使用测试 设备&#xff1a;IBM3550服务器交换机&#xff0c; 移动硬盘&#xff1a;附加存储盘&#xff0c; u盘1&#xff1…

GCN+BiLSTM多特征输入时间序列预测(Pytorch)

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 GCNBiLSTM多特征输入时间序列预测&#xff08;Pytorch&#xff09; 可以做风电预测&#xff0c;光伏预测&#xff0c;寿命预测&#xff0c;浓度预测等。 Python代码&#xff0c;基于Pytorch编写 1.多特征输入单步预测…

sersync实时同步部署案例

目录 sersync介绍 案例信息 操作步骤 服务端部署 客户端部署 创建存储目录 安装sersync 修改配置文件 启动服务 停止服务 测试 sersync介绍 sersync是一个基于inotifyrsync的实时文件同步工具&#xff0c;通过监控目录的变动达到实时同步的目的。 案例信息 拓扑…

ELK之路第一步——Elasticsearch集群的搭建以及踩坑记录

elasticSearch集群 前言一、架构二、下载三、虚拟机相关设置3.1 创建es用户3.2 为建es用户赋权sudo3.3 更换es目录所属用户 四、Elasticsearch配置文件修改4.1 修改elasticsearch.yml4.2 修改jvm.options4.3 修改jdk路径 五、启动六、启动报错七、可视化界面cerebro 前言 Elk&…

SwiftUI:单个App支持设置多语言

SwiftUI 全新多语言方案 简化本地化的字符串- WWDC21 - 视频 本地化您的SwiftUI app - WWDC21 - 视频 构建全球化App&#xff1a;本地化的示例- WWDC22 - 视频 构建支持多语言的App - WWDC24 - 视频 单个App支持设置多语言 工程 Info.plist里添加 键值UIPrefersShowingLangua…

go 使用fyne实现桌面程序的计算器例子

使用Fyne工具包构建跨平台应用是非常简单的&#xff0c;在此之前我们需要做一些准备功能做&#xff0c;比如安装一些gcc基础图形依赖库&#xff0c;还有go语言本身的运行开发环境都是必要的。 在此之前我们希望你是go语言的已入门用户&#xff0c;掌握go的协程&#xff0c;管道…

【C++进阶篇】——STL的简介

【C进阶篇】——STL的简介 1.什么是STL STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复用的组件库&#xff0c;而且是一个包罗数据结构与算法的软件框架。 2.STL的版本 原始版本 Alexander Stepanov、Meng Lee 在…

【Linux系统】环境变量

一、通俗例子解释环境变量概念与作用 想象你在厨房做饭&#xff0c;需要找到各种调料和工具。这些调料和工具就相当于计算机中的“资源”&#xff0c;而环境变量就像厨房里的一本规则书&#xff0c;里面列出了厨房里所有调料和工具的位置。 具有全局性&#xff08;所有人都能用…

MCU SCT文件学习

硬件&#xff1a;GD32F427, 外设&#xff1a;FreeRtos,ADC,DMA 现象&#xff1a;注释掉一些线程&#xff0c;发现ADC采样的线程采样不到数据了。 开始猜测是线程重入&#xff0c;破坏了地址&#xff0c;后来思考一下发现不对&#xff0c;因为注释掉线程&#xff0c;ADCDMA采…

【Vulnhub靶场】DC-4

DC-4靶场下载地址https://www.five86.com/downloads/DC-4.zip 本机IP&#xff1a;192.168.118.128 靶机IP&#xff1a;192.168.118.0/24 信息收集 扫描主机存活&#xff0c;扫描端口&#xff0c;扫描服务 第一步扫描出主机ip为192.168.118.141 nmap -sP 192.168.118.0/24 nm…

高级 SQL 技巧全面教程:提升你的数据库操作能力

高级 SQL 技巧全面教程&#xff1a;提升你的数据库操作能力 引言 在数据驱动的时代&#xff0c;SQL&#xff08;结构化查询语言&#xff09;是与数据库交互的核心工具。虽然基础 SQL 语法足以满足日常需求&#xff0c;但掌握一些高级技巧能够显著提高你的工作效率和数据处理能…