把STL容器放入共享内存,重用STL allocator,传入模板参数Allocator,可以实现

问题

Q: 如何用共享内存来存放C++ STL中中的容器?
A: 传入自定义的申请共享内存上空间的allocator,见模板参数Allocator

参考

https://www.zhihu.com/question/319108981/answer/649050789
https://en.cppreference.com/w/cpp/container/vector
http://www.cppblog.com/doing5552/archive/2010/07/24/121197.html

文章目录

  • 问题
  • 参考
  • 分析内容
    • 调用POSIX 函数申请共享内存示例
    • V系统调用的方式申请共享内存
    • 存放在共享内存中的数据结构应该简单
    • 共享内存中的STL容器,有困难
    • 重用STL allocator,传入模板参数Allocator,可以实现
  • 结论:只要提供一个用户自定义的allocator,任何STL容器都可以安全的放置到共享内存上。
  • 正式干活
    • 1、设计一个 allocator
    • 2、容器在共享内存中的确定地址
      • 2.1、方式1
      • 2.2、方式2
      • 3 代码资源

分析内容

共享内存(shm)允许多个进程把同一块物理内存段(segment)映射(map)到它们的地址空间中去。顾名思义,共享内存就是进程之间共享的一组内存段。当一个进程附着到一块共享内存上后,它得到一个指向这块共享内存的指针;该进程可以像使用其他内存一样使用这块共享内存。当然,由于这块内存同样会被其他进程访问或写入,所以必须要注意进程同步问题。

参考如下代码,这是UNIX系统上使用共享内存的一般方法(注:本文调用的是POSIX函数):

调用POSIX 函数申请共享内存示例

可以看在上一篇博客多线程之间、使用共享内存、实现图片的数据通信:包括POSIX共享内存(shm_open 和 mmap虚拟内存)、系统调用(shmat物理存储器)、内存映射文件等方法中说了,处理POSIX以外,还可以使用系统调用。

V系统调用的方式申请共享内存

//Get shared memory id
//shared memory key
const key_t ipckey = 24568;
//shared memory permission; can be
//read and written by anybody
const int perm = 0666;
//shared memory segment size
size_t shmSize = 4096;
//Create shared memory if not
//already created with specified
//permission
int shmId = shmget(ipckey,shmSize,IPC_CREAT|perm);
if (shmId ==-1)
{//Error
}//Attach the shared memory segmentvoid* shmPtr = shmat(shmId,NULL,0);struct commonData* dp =  (struct commonData*)shmPtr;//detach shared memory
shmdt(shmPtr);

存放在共享内存中的数据结构应该简单

按照博客的说法,结构 commonData 的成员 name 和指向下一个结构的 next 所指向的内存分别从进程A的地址空间中的堆上分配,这种方式无法在另外一个进程使用,当进程B访问 dp->name 或者 dp->next 时候,由于它在访问自己地址空间以外的内存空间,所以这将是非法操作(memory violation),它无法正确得到 name和 next 所指向的内存。

如下结构:

struct commonData
{int sharedInt;float  sharedFloat;char* name;Struct CommonData* next;
};

进程A把数据写入共享内存:

//Attach shared memory
struct commonData* dp =  (struct commonData*)shmat(shmId,NULL,0);dp->sharedInt = 5;
.
.
dp->name = new char [20];
strcpy(dp->name,"My Name");dp->next = new struct commonData();

稍后,进程B把数据读出:

struct commonData* dp =  (struct commonData*)shmat(shmId,NULL,0);//count = 5;
int count = dp->sharedInt;
//problem
printf("name = [%s]\n",dp->name);
dp = dp->next;  //problem

结论:放入共享内存中的结构应该简单。(注:我觉得最好避免使用指针)

共享内存中的STL容器,有困难

如果把STL容器,例如map, vector, list放入共享内存中,就没必要再为共享内存设计其他额外的数据结构。STL容器被良好的封装,默认情况下有它们自己的内存管理方案。当一个元素被插入到一个STL列表(list)中时,列表容器自动为其分配内存,保存数据。考虑到要将STL容器放到共享内存中,而容器却自己在堆上分配内存,下面的实验证明了直接在共享内存放容器搞不来,会报错:

//Attach to shared memory
void* rp = (void*)shmat(shmId,NULL,0);
//Construct the vector in shared memory using placement new
vector<int>* vpInA = new(rp) vector<int>*;
//The vector is allocating internal data
//from the heap in process A's address
//space to hold the integer value
(*vpInA)[0] = 22;

然后进程B希望从共享内存中取出数据:

vector<int>* vpInB =  (vector<int>*) shmat(shmId,NULL,0);//problem - the vector contains internal
//pointers allocated in process A's address
//space and are invalid here
int i = *(vpInB)[0];

矢量包含在进程A的地址空间中分配的内部指针,在此处无效。

重用STL allocator,传入模板参数Allocator,可以实现

传入自定义的申请共享内存上空间的allocator,见模板参数Allocator

进一步考察STL容器,我们发现它的模板定义中有第二个默认参数,也就是allocator 类,该类实际是一个内存分配模型。默认的allocator是从堆上分配内存(注:这就是STL容器的默认表现,我们甚至可以改造它从一个网络数据库中分配空间,保存数据)。下边是 vector 类的一部分定义:

template<class T, class A = allocator<T> >
class vector
{//other stuff
};

考虑如下声明:

//User supplied allocator myAlloc
vector<int,myAlloc<int> > alocV;

假设 myAlloc 从共享内存上分配内存,则 alocV 将完全在共享内存上被构造,所以进程A可以如下:

//Attach to shared memory
void* rp = (void*)shmat(shmId,NULL,0);
//Construct the vector in shared memory
//using placement new
vector<int>* vpInA =new(rp) vector<int,myAlloc<int> >*;
//The vector uses myAlloc<int> to allocate
//memory for its internal data structure
//from shared memory
(*v)[0] = 22;

进程B可以如下读出数据:

vector<int>* vpInB = (vector<int,myAlloc<int> >*) shmat(shmId,NULL,0);//Okay since all of the vector is
//in shared memory
int i = *(vpInB)[0];

所有附着在共享内存上的进程都可以安全的使用该vector。在这个例子中,该类的所有内存都在共享内存上分配,同时可以被其他的进程访问。只要提供一个用户自定义的allocator,任何STL容器都可以安全的放置到共享内存上。

结论:只要提供一个用户自定义的allocator,任何STL容器都可以安全的放置到共享内存上。

正式干活

1、设计一个 allocator

基于共享内存的STL Allocator,shared_allocator.hh 是一个STL Allocator的实现,SharedAllocator 是一个模板类。而 Pool 类完成共享内存的分配与回收。

您所描述的pool.hhshmPool类似乎是用于管理共享内存的一个封装。下面我会基于您给出的描述来详细解释各个部分:

  1. Pool 类定义
    Pool是更广泛的类
    shmPoolPool的一个具体实现

  2. 静态成员 shm_

    • shm_是一个静态成员,其类型是shmPool
    • 静态成员意味着它在所有类的实例之间共享,并且只存在一个副本。因此,不论你创建了多少个Pool(或shmPool)的实例,它们都共享同一个shm_对象。
    • 这确保了每个进程(如果每个进程都访问这个静态成员)都只有一个shmPool的实例,这对于共享内存的管理是非常重要的。
  3. shmPool 构造函数

    • 构造函数用于初始化shmPool对象。
    • 在这个过程中,它会创建并附着所需大小的内存到共享内存上。
    • 共享内存的参数(如键值、段数目、段大小)通过环境变量传递给构造函数。这样,你可以在不修改代码的情况下,通过调整环境变量来改变共享内存的行为。
  4. 成员变量

    • segs_:表示共享段的数目。
    • segSize_:表示每个共享段的大小。
    • path_key_:用于创建唯一的IPC键(可能是POSIX IPC或System V IPC)。这个键用于标识和访问共享内存。
  5. 信号量(Semaphore)

    • shmPool为每个共享段创建一个信号量,用于同步。这意味着当多个进程或线程需要访问同一个共享段时,它们会通过信号量来协调,以确保数据的一致性和完整性。
  6. Chunk 类

    • Chunk类代表一个共享段。这意味着shmPool可能包含多个Chunk对象,每个对象管理共享内存中的一个段。
    • 每个Chunk都有一个唯一的shmId_(共享内存ID),用于标识和访问该段。
    • semId_是一个信号量ID,用于控制对该段的访问许可。
    • Link结构(或类的指针)可能用于表示该Chunk中可用的内存块或段的链接列表。这样,当你需要分配或释放内存时,你可以通过这个链接列表来有效地管理内存。

shmPool类似乎是一个封装了共享内存管理的类,它使用静态成员来确保每个进程只有一个实例,使用信号量来同步访问,并使用Chunk类来管理共享内存的各个段。这种设计使得在多进程或多线程环境中使用共享内存变得更加简单和安全。

2、容器在共享内存中的确定地址

2.1、方式1

假设进程A在共享内存中放入了数个容器,进程B如何找到这些容器呢?一个方法就是进程A把容器放在共享内存中的确定地址上(fixed offsets),则进程B可以从该已知地址上获取容器。

2.2、方式2

假设进程A在共享内存中放入了数个容器,进程B如何找到这些容器呢?一个方法,进程A先在共享内存某块确定地址上放置一个map容器,然后进程A再创建其他容器,然后给其取个名字和地址一并保存到这个map容器里。进程B知道如何获取该保存了地址映射的map容器,然后同样再根据名字取得其他容器的地址。

3 代码资源

本文描述的方案可以在共享内存中创建STL容器,其中的一个缺陷是,在分配共享内存之前,应该保证共享内存的总大小(segs_* segSize_)大于你要保存STL容器的最大长度,因为一旦类Pool 超出了共享内存的,该类无法再分配新的共享内存。

见绑定资源

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

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

相关文章

自定义User-Agent:使用Python Requests进行网络请求

在网络编程和数据采集领域&#xff0c;HTTP请求是与服务器交互的基本方式。User-Agent&#xff08;用户代理&#xff09;是HTTP请求中的一个重要字段&#xff0c;它告诉服务器发起请求的客户端类型和版本信息。在某些情况下&#xff0c;自定义User-Agent可以帮助我们模拟不同的…

CodeIgniter学习笔记 Item6--CI中的常规主题_ci中parent __construct();

$route[default\_controller] welcome; $route[404\_override] ;更重要的功能是&#xff0c;如果我们要实现通过/index.php/news/4.html访问/index.php/article/show/1这样一个需求&#xff0c;可以在routes.php定义路由关系&#xff0c;将原始的URL转换成需要样式 $route[n…

QtSingleApplication

​​​​​​/qtsingleapplication/ qtSingleApplication使用总结-CSDN博客 示例&#xff1a; #include "mainwindow.h" #include <QApplication> #include <QMessageBox> #include "qtsingleapplication/qtsingleapplication.h" int main(…

正向代理与反向代理:深入解析与实例讲解

代理服务器是一种网络实体&#xff0c;它充当客户端与服务器之间的中介&#xff0c;负责转发请求和响应。代理服务器可以根据其使用方式和功能的不同&#xff0c;分为正向代理和反向代理。 转转的面试中问到了正向代理和反向代理。所以本篇文章将详细解析这两种代理方式&#…

产品干货 | 如何通过Power Platform快速创建自定义连接器,让开发集成更简单

引言 Introduction&#xff1a; 最近&#xff0c;微软推出了许多新产品功能。迅易科技作为微软13年来紧密的生态合作伙伴&#xff0c;为300行业头部客户实施1000项目。为此&#xff0c;我们总结了多年的项目经验且产品学习心得&#xff0c;邀请了迅易技术专家为大家持续分享微软…

Oracle数据库之PL/SQL例外(二十三)

在Oracle PL/SQL中&#xff0c;例外&#xff08;或异常&#xff09;是程序设计语言提供的一种功能&#xff0c;旨在增强程序的健壮性和容错性。Oracle PL/SQL中的例外可以分为两大类&#xff1a;系统定义例外和自定义例外。 1. 系统定义例外 系统定义例外是Oracle数据库预先定…

不同系統和流覽器如何檢查代理設置(Mac/Windows/Linux)

本文將詳細介紹如何檢查代理設置&#xff0c;並提供一些建議&#xff0c;幫助你更好地管理和優化代理使用體驗。 為什麼需要檢查代理設置&#xff1f; 代理伺服器的主要功能之一是保護用戶的隱私。如果代理設置不正確&#xff0c;可能會暴露你的真實IP地址&#xff0c;導致隱…

如何自己录制教学视频?零基础也能上手

随着在线教育的蓬勃发展&#xff0c;录制教学视频成为了教师和教育工作者们不可或缺的一项技能。无论是为了远程教学、课程分享还是知识普及&#xff0c;教学视频的录制都变得愈发重要。可是如何自己录制教学视频呢&#xff1f;本文将介绍两种录制教学视频的方法&#xff0c;这…

linux绝对路径与相对路径区别简述

绝对路径与相对路径定义 绝对路径&#xff1a;相对于根路径&#xff0c;只要文件不移动位置&#xff0c;那么它的绝对路径是永恒不变的 相对路径&#xff1a;相对于当前所在目录而言&#xff0c;当前所在的目录可能会改变&#xff0c;所以相对路径不是固定的 路径&#xff…

Availability

译文&#xff1a; #ifndef __AVAILABILITY__ #define __AVAILABILITY__ /* 这些宏都是用在头文件种。它们是作用于和系统版本有关的函数声明或者函数&#xff0c;如果它们是可用的就标示它们首次有效的方法&#xff1b;或者被抛弃的函数。 MAC OS 和_IOS_ 有不同的版本号&…

uniapp小程序button按钮去掉黑色线条

文章目录 导文去除方法 导文 在uniapp的button按钮中有一个莫名其妙的黑色线条 去除方法 button::after{border: none; }该问题并不是所有的手机机型都有&#xff0c;最好添加一下&#xff0c;以防用户手机端样式有问题。 您好&#xff0c;我是肥晨。 欢迎关注我获取前端学习…

Google浏览器快捷方式固定到任务栏启动被其他网页劫持

场景复现 1、Google浏览器设置启动时继续浏览上次打开的网页 2、先浏览CSDN网站&#xff0c;然后关闭Google浏览器 3、再次打开Google浏览器时&#xff0c;除了显示我们上次浏览的CSDN网页外&#xff0c;还默认打开了百度网页 解决办法 1、在Google浏览器中新建标签页&am…

hexo 实战:(三)独立博客运营总结

前言 通过前面两个步骤&#xff0c;完成了静态博客从搭建到基本功能添加和界面优化。那么&#xff0c;这次就要介绍一下搭在 GitHub 上的静态博客如何运营推广。也就是如何让网站被各大搜索引擎收录&#xff1f;如何提高网站权重&#xff0c;提高收录量&#xff1f; 网站收录 …

神经网络 #数据挖掘 #Python

神经网络是一种受生物神经元系统启发的人工计算模型&#xff0c;用于模仿人脑的学习和决策过程。它由大量互相连接的节点&#xff08;称为神经元&#xff09;组成&#xff0c;这些节点处理和传递信息。神经网络通常包含输入层、隐藏层&#xff08;可有多个&#xff09;和输出层…

2024版 空间杜宾模型和检验代码+结果解释

空间误差、空间滞后、空间杜宾模型&#xff0c;筛选过程 莫兰指数&#xff0c;LM检验&#xff0c;LR检验&#xff0c;WALD检验&#xff0c;代码&#xff0c;及解释用法 空间溢出直接效应间接效应等等空间计量全过程。 原文链接https://mp.weixin.qq.com/s?__bizMzUyNzczMTI4…

旋转机械振动信号特征提取(Python)

前缀 &#xff1a;将一维机械振动信号构造为训练集和测试集&#xff08;Python&#xff09; https://mp.weixin.qq.com/s/DTKjBo6_WAQ7bUPZEdB1TA import pandas as pd import numpy as np import scipy.io as sio import statistics_hamming from statistics_hamming import…

掌握这些 Windows 截图工具:猫头虎带你解析 ShareX、PicPick、FastStone 和 Snagit

掌握这些 Windows 截图工具&#xff1a;猫头虎带你解析 ShareX、PicPick、FastStone 和 Snagit 前言 在日常办公、学习和内容创作中&#xff0c;截图是我们经常需要用到的功能。然而&#xff0c;面对各种不同的截图需求&#xff0c;如全屏截图、区域截图、滚动截图等&#xf…

被年轻人买爆的转运能量石,戴一天竟等于拍千次胸片?

离谱的事年年有&#xff0c;这几年可以说非常多&#xff01;‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ 尤其是这届年轻人&#xff0c;不知道什么时候开始&#xff0c;越来越迷信了&#xff01; 比如去年很…

[word] word设置上标快捷键 #学习方法#其他#媒体

word设置上标快捷键 办公中&#xff0c;少不了使用word&#xff0c;这个是大家必备的软件&#xff0c;今天给大家分享word设置上标快捷键&#xff0c;希望在办公中能帮到您&#xff01; 1、添加上标 在录入一些公式&#xff0c;或者是化学产品时&#xff0c;需要添加上标内容…

华为数通——链路聚合

链路聚合&#xff1a;又称为端口汇聚&#xff0c;是指两台交换机之间在物理上将两个或多个端口连接起来&#xff0c;将多条链路聚合成一条逻辑链路&#xff0c;从而增大链路带宽&#xff0c;多条物理链路之间能够相互冗余。 作用&#xff1a;增加链路带宽&#xff0c;同时提供…