Boost共享内存/内存映射文件构建容器的封装 可作为对象序列化使用

转自:https://anonymalias.github.io/2016/01/14/boost_ipc_pack/

Boost共享内存/内存映射文件构建容器的封装

文章目录

  1. 1.首先是对boost ipc的托管内存段进行封装,如下:
  2. 2.其次是对要构建的各个容器进行封装,这里举例map的封装:
  3. 3.在托管内存段上进行容器的构建如下:

本人github主站:https://anonymalias.github.io
本人csdn主站:http://blog.csdn.net/anonymalias

Boost Interprocess组件提供了通过了托管内存段(managed_mapped_file & managed_shared_memory)在共享内存和内存映射文件上进行复杂数据结构构建的功能。

通过boost.ipc构造简单的POD纯数据结构, 非常的简单

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <cstdio>
#include <boost/interprocess/managed_mapped_file.hpp>

namespace bipc = ::boost::interprocess;
typedef bipc::managed_mapped_file managed_mapped_file_t;
typedef bipc::managed_mapped_file::segment_manager mapped_segment_manager_t;

struct Msg 
{
    uint32_t id; 
    char name[128];
    uint32_t age;
};

int main()
{
    managed_mapped_file_t obj_mapped_file(bipc::open_or_create, "./msg_map.mmap", 1024*1024);
    Msg * p_msg = obj_mapped_file.find_or_construct<Msg>("msg_map")();
    if(NULL == p_msg)
    {   
        std::cerr<<"construct msg failed"<<std::endl;
        return -1; 
    }   

    std::cout<<p_msg->id<<std::endl;
    std::cout<<p_msg->name<<std::endl;
    std::cout<<p_msg->age<<std::endl;

    p_msg->id = p_msg->id + 1;
    strcpy(p_msg->name, "hello world!");
    p_msg->age = p_msg->age + 1;

    return 0;
}

 

在项目过程中,更多的时候是需要在共享内存/内存映射文件上构建复杂的容器。我们知道一般

  • 容器的分配器:容器的构造是直接在进程的地址空间(堆)上进行的,容器的分配器allocator都是默认构造的,所以其分配器可认为是无状态的(stateless)。
  • boost.ipc的分配器:需要从具体的系统内核内存片段上进行内存的分配,而不是进程的内存资源,所以boost.ipc的分配器是有状态的(stateful), 言外之意,需要告知boost.ipc的分配器,在哪里进行内存分配,必须传入共享内存,或是内存映射文件。

所以要在boost.ipc上构造容器,需要将boost.ipc的分配器作为容器的allocator传入容器,让容器知道在ipc上进行内存分配和对象构造。Boost ipc的allocator内的get_segment_manager()可以返回allocator构造时传入的内存片段管理器。(注意:托管内存段(managed_mapped_file & managed_shared_memory)的内存片段管理器segment_manager 本质是一个allocator,在文章http://blog.csdn.net/anonymalias/article/details/50496563有详解)。

所以现在在boost.ipc上构建一个map,需要有两步操作(这里已mapped file举例):

  • 构建mapped_file对象。
  • 通过mapped_file对象,构建map,在构建map的时候需要传入mapped_file的分配器。

代码如下:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>

#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/allocators/node_allocator.hpp>

#include <boost/container/string.hpp>

namespace bipc = ::boost::interprocess;
typedef bipc::managed_mapped_file managed_mapped_file_t;
typedef bipc::managed_mapped_file::segment_manager mapped_segment_manager_t;

struct Msg 
{
    uint32_t id; 
    bipc::string name;
    uint32_t age;
};

typedef std::pair<const uint32_t, Msg> pair_t;
typedef bipc::node_allocator<pair_t, mapped_segment_manager_t> allocator_t;
typedef std::less<uint32_t> less_t;

typedef bipc::map<uint32_t, Msg, less_t, allocator_t> msg_map_t;
typedef msg_map_t::iterator map_iter_t;

int main()
{
    managed_mapped_file_t obj_mapped_file(bipc::open_or_create, "./msg_map_boost.mmap", 1024*1024);
    msg_map_t *p_msg_map = obj_mapped_file.find_or_construct<msg_map_t>("msg_map")(less_t(), \
            obj_mapped_file.get_segment_manager());
    if(NULL == p_msg_map)
    {   
        std::cerr<<"construct msg_map failed"<<std::endl;
        return -1; 
    }   

    for(int i = 0; i < 10; ++i)
    {   
        map_iter_t itr = p_msg_map->find(i);
        if(itr == p_msg_map->end())
        {   
            std::cout<<"not find:"<<i<<" insert:"<<i<<std::endl;
            Msg msg = {i, "alias", 100+i};
            p_msg_map->insert(std::pair<uint32_t, Msg>(i, msg));
        }   
        else
        {   
            std::cout<<"find:"<<i<<" data:"<<itr->second.name<<" "<<\
                itr->second.age<<std::endl;
        }   
    }   
    return 0;
}

 

为了代码的可读性,需要定义一堆的typedef来指定待构建map的allocator的构造传参是一个boost ipc的segment_manager。当map的value不是POD,而是嵌套其他容器是,你将会看到这样的定义:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
namespace bipc = ::boost::interprocess;
typedef bipc::managed_mapped_file managed_mapped_file_t;
typedef bipc::managed_mapped_file::segment_manager mapped_segment_manager_t;

typedef bipc::node_allocator<float, mapped_segment_manager_t> vec_allocator_t;
typedef boost::container::vector<float, vec_allocator_t> vector_t;

struct Msg 
{
    Msg(const vec_allocator_t &vec_alloc) : score(vec_alloc){}

    uint32_t id; 
    bipc::string name;
    uint32_t age;
    vector_t score;
};

typedef std::pair<const uint32_t, Msg> pair_t;
typedef bipc::node_allocator<pair_t, mapped_segment_manager_t> allocator_t;
typedef std::less<uint32_t> less_t;

typedef bipc::map<uint32_t, Msg, less_t, allocator_t> msg_map_t;
typedef msg_map_t::iterator map_iter_t;

 

程序员的天性(C++程序员。。。), tigerlan我的导师为了不愿意造轮子,对在boost ipc上进行容器构造的逻辑进行了封装:那我写这篇文章还有毛意义,又不是我造的轮子,其实使用过程中,还是发现一些问题,算是对自己的一个提升吧,写这篇文章的本意也是想让自己记录一下过程中遇到的问题和细节,已经和大家分享的过程。

1.首先是对boost ipc的托管内存段进行封装,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <functional>
#include <utility>
#include <string>

typedef boost::interprocess::managed_mapped_file mapped_file_t;
typedef boost::interprocess::managed_shared_memory shared_memory_t;

template <typename segment>
class Segment
{
public:
    typedef segment segment_t ;
    typedef typename segment_t::segment_manager segment_manager_t ;
public:
    Segment(const char * name, size_t size):
        m_name(name),m_size(size),
        m_segment(boost::interprocess::open_or_create, m_name.c_str(), m_size )
    { }

    segment_t & operator () ()
    {
        return m_segment;
    }
    /*
    ...省略
    */
private:
    std::string m_name;
    size_t m_size;
    segment_t m_segment;
};

typedef Segment< mapped_file_t > MapedFile;
typedef Segment< shared_memory_t > SharedMemory;

2.其次是对要构建的各个容器进行封装,这里举例map的封装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <boost/interprocess/allocators/node_allocator.hpp>
#include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/offset_ptr.hpp>
#include <functional>
#include <utility>

#include "segment.hpp"

namespace bip = boost::interprocess;

template <typename key_t,typename data_t, typename segment_t>
class SegmentMap
{
    typedef typename segment_t::segment_manager_t segment_manager_t;
    typedef typename std::pair<const key_t, data_t> pair_t;
    typedef typename std::less<key_t> less_t;
    typedef typename bip::node_allocator <pair_t , segment_manager_t> allocator_t;

public:
    typedef typename bip::map<key_t, data_t, less_t ,allocator_t> map_t;
    typedef typename map_t::iterator iterator;

public:
    SegmentMap(const char * name, segment_t & segment ) :
        m_segment( segment ),
        m_allocator( m_segment().get_segment_manager() )
    {
        m_segment_map = m_segment().template find_or_construct<map_t>
        (name) (less_t(),m_allocator);

        assert(m_segment_map != NULL);
    }

    SegmentMap(segment_t & segment) :
        m_segment( segment ),
        m_allocator( m_segment().get_segment_manager() )
    {
        m_segment_map = m_segment().template find_or_construct< map_t >
            ( bip::anonymous_instance ) (less_t(), m_allocator);
        assert(m_segment_map != NULL);
    }

    ~SegmentMap(){};

    map_t & operator () ()
    {
        return *m_segment_map;
    }

    /*
    ...省略
    */

private:
    segment_t & m_segment;
    allocator_t m_allocator;
    bip::offset_ptr<map_t> m_segment_map;
};

3.在托管内存段上进行容器的构建如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include "iostream"

#include "segment.hpp"
#include "segment_map.hpp"
#include "segment_list.hpp"

typedef MapedFile _Memory;
_Memory * g_memory = NULL;

struct Company
{
    typedef SegmentList<uint32_t, _Memory> leader_list_t;
    typedef SegmentList<uint32_t, _Memory> colleague_list_t;
    leader_list_t leader_list;
    colleague_list_t colleague_list;

    Company() : leader_list(*g_memory), colleague_list(*g_memory){}
};

typedef SegmentMap<uint32_t, Company, _Memory> company_map_t;

int main()
{
    g_memory = new _Memory("test.mmap", 1024 * 1024);

    company_map_t * p_company_map = new company_map_t("test2", *g_memory);
    company_map_t::iterator itr;

    for(int i = 0; i < 10; ++i)
    {   
        itr = (*p_company_map)().find(i);
        if(itr == (*p_company_map)().end())
        {   
            std::cout<<"not find:"<<i;

            Company &obj = (*p_company_map)[i];
            obj.leader_list().push_back(i + 100);

            std::cout<<"insert:"<<i + 100<<std::endl;
        }   
        else
        {   
            std::cout<<"find:"<<i;
            for(Company::leader_list_t::iterator list_itr = itr->second.leader_list().begin(); 
                    list_itr != itr->second.leader_list().end(); ++list_itr)
            {   
                std::cout<<" value:"<<*list_itr;
            }   

            std::cout<<std::endl;
        }   
    }   
}

可以看出利用封装过后的Segment和SegmentList, SegmentMap,进行容器的构建是很方便的一件事情,需要注意的是map的value:struct Company的构造需要传入boost ipc的分配器,以便内部的list对象进行构造。所已需要Company的默认构造函数,无参,且参数初始化列表传入全局的已初始化的托管内存段对象,否则不能map的[]操作符。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
共享内存是一种用于多个进程之间进行数据交换的机制。共享内存封装类是为了方便开发者使用共享内存而设计的类。它封装了底层的共享内存操作,提供了一些简洁易用的接口供开发者调用。 在C++中,可以使用第三方库例如Boost库或者Qt库提供的共享内存封装类,也可以自己编写封装类。无论是使用第三方库还是自己编写封装类,共享内存封装类通常会提供以下功能: 1. 创建或打开共享内存封装类提供了一些方法用于创建或打开已存在的共享内存区域。 2. 写入和读取数据:封装类提供了方法用于往共享内存中写入数据或从共享内存中读取数据。 3. 同步机制:封装类通常提供了一些同步机制,例如互斥锁或条件变量,来确保多个进程之间对共享内存的读写操作是安全的。 一个使用共享内存封装类的实例是_vc内存文件类。_vc内存文件类是属于Qt库中的一部分,用于在不同的进程之间共享数据。它提供了一些方法来创建、打开、写入和读取共享内存中的数据。 使用_vc内存文件类时,首先需要创建一个_vc内存文件对象,然后可以调用其方法来创建或打开已存在的共享内存区域。创建或打开成功后,就可以使用对象的方法来进行数据的写入和读取操作。 例如,可以调用_vc内存文件对象的writeData()方法来写入数据,该方法会将数据写入共享内存中的指定位置;而调用readData()方法可以从共享内存中读取数据。 在使用_vc内存文件类时,还可以利用其提供的同步机制,例如setLock()方法用于设置互斥锁,以确保多个进程之间对共享内存的操作是互斥的,同时可以使用条件变量来进行进程间的通信。 总之,共享内存封装类和_vc内存文件类是用于方便开发者使用共享内存的工具,通过封装底层操作,使得开发者可以更方便地进行数据共享和交换。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值