boost::interprocess::managed_mapped_file 如何包含弹性向量?

Posted

技术标签:

【中文标题】boost::interprocess::managed_mapped_file 如何包含弹性向量?【英文标题】:How can boost::interprocess::managed_mapped_file contain a elastic vector? 【发布时间】:2020-01-09 00:38:57 【问题描述】:

我的理解是这段代码可以在文件中保存一个向量。

using Alloc = boost::interprocess::allocator<int32_t, boost::interprocess::managed_mapped_file::segment_manager>;
using Vec = boost::interprocess::vector<int32_t, Alloc>;

void f() 
    boost::interprocess::managed_mapped_file seg(boost::interprocess::open_or_create, "data.dat", 1024);
    auto vec = seg.find_or_construct<Vec>("vec")(seg.get_segment_manager());

    // ......


但是,当向量试图获取太多字节时,它会抛出 bad_alloc 异常。

我无法预测需要多少字节,所以我想出在 free_memory 减少时增加文件。但是,有时会在增长时发生断言失败。 这是最少的代码。

using Alloc = allocator<int32_t, managed_mapped_file::segment_manager>;
using Vec = vector<int32_t, Alloc>;

void test(int, int);
int main()
   
    test(4096, 4); // Change this value if not reproduced
    return 0;


void test(int initialBytes, int extraBytes) 
    fprintf(stderr, "test(%d, %d)\n", initialBytes, extraBytes);

    remove("data.dat");

    auto seg = make_unique<managed_mapped_file>(open_or_create, "data.dat", initialBytes);
    auto vec = seg->find_or_construct<Vec>("vec")(seg->get_segment_manager());
    fprintf(stderr, "vec->capacity=%ld\n", vec->capacity());
    fprintf(stderr, "seg.get_free_memory()=%ld\n\n", seg->get_free_memory());

    seg = nullptr;
    fprintf(stderr, "tag1\n");
    boost::interprocess::managed_mapped_file::grow("data.dat", extraBytes);

    fprintf(stderr, "tag2\n");
    seg = make_unique<managed_mapped_file>(open_only, "data.dat");

    fprintf(stderr, "tag3\n");
    vec = seg->find<Vec>("vec").first;

    fprintf(stderr, "tag4\n");

    fprintf(stderr, "vec->capacity=%ld\n", vec->capacity());
    fprintf(stderr, "seg.get_free_memory()=%ld\n\n", seg->get_free_memory());


更小

void test(int initialBytes, int extraBytes) 
    remove("data.dat");
    auto seg = std::make_unique<managed_mapped_file>(open_or_create, "data.dat", initialBytes);
    seg = nullptr;
    managed_mapped_file::grow("data.dat", extraBytes);

问:如何在不假设最大尺寸的情况下存储弹性向量?


我误解了grow - 它是静态函数。所以我修复了代码。断言仍然失败。


我发现4 &lt;= extraBytes &amp;&amp; extraBytes &lt; 24时断言失败

【问题讨论】:

通过编写自己的(复杂的)实现?共享内存映射总是根据所涉及的有限页数来定义。要实现真正的动态行为,您必须创建额外的映射。这与“正常” malloc 的工作方式并没有什么不同(在某个时间点,它还必须将内存“映射”到进程空间才能完成进一步的分配)。 @oakad 那么grow 做了什么?它不会关闭 mmf 并展开吗? 在任何共享内存情况下都涉及多方。所有这些各方都必须以某种方式就共享内存范围进行同步。至于grow,boost 文档非常清楚:只有在相关内存段未被任何人使用(0 个相关方)时它才有效。 boost.org/doc/libs/1_72_0/doc/html/interprocess/… @oakad 当我发现grow 是静态的时,我认为seg 必须被释放。然后我修复了代码,但没有任何改变。除了共享内存与 mmf 之外,我在链接上找不到我的代码和示例之间的区别。 【参考方案1】:

您可以在下面看到工作示例。该程序创建小型内存映射文件,创建向量容器,然后通过 push_back 填充向量。如果无法进行下一个 push_back,则将控制转移到 catch 块。在这个块中关闭文件,调用函数grow,打开内存映射文件,创建向量并重复push_back。

#include <iostream>
#define BOOST_DATE_TIME_NO_LIB
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <string>

namespace IP = boost::interprocess;
template <typename T> using MyVectorT = IP::vector < T, IP::allocator<T, IP::managed_mapped_file::segment_manager>>;

int main(int )

    const char *FileName = "file.bin";
    const std::size_t FileSize = 1000;
    using MyVector = MyVectorT<size_t>;
    try
    
        IP::remove_file_on_destroy tmp FileName ;
        IP::managed_mapped_file segment(IP::create_only
            , FileName      //Mapped file name
            , FileSize);    //Mapped file initial size

        MyVector *myvector = segment.construct<MyVector>("MyVector")(segment.get_segment_manager());
        for (size_t i = 0; i < 100000; ++i)  //Insert data in the vector
        
            bool push_failure = true;
            do
            
                try
                
                    myvector->push_back(i);
                    push_failure = false;       //success of push_back
                
                catch (const IP::bad_alloc &)   //memory mapped file is too small for vector
                
                    const size_t grow_size = std::max<size_t>(FileSize, 2 * (myvector->size() + 1) * sizeof(MyVector::value_type));   //estimate memory for new vector capacity
                    std::cout << "segment size = " << segment.get_size() << " Vector capacity = " << myvector->capacity() << " grow_size = " << grow_size;
                    //free memory mapped file
                    segment.flush();    
                    segment.~basic_managed_mapped_file();
                    IP::managed_mapped_file::grow(FileName, grow_size);
                    new (&segment) IP::managed_mapped_file(IP::open_only, FileName);
                    std::cout << " -> new segment size = " << segment.get_size() << std::endl;
                    myvector = segment.find<MyVector>("MyVector").first;
                    push_failure = true;        //try push_back again!!!
                
             while (push_failure);
        
        std::cout << "Vector size =" << myvector->size() << "\n";
        for (size_t i = 0; i < 100000; ++i)
        
            if ((*myvector)[i] != i)
            
                std::cout << "vector error!!! i = " << i << " vector[i] = " << (*myvector)[i] << std::endl;
            
        
    
    catch (const std::exception &e)
    
        std::cout << "Error " << e.what() << std::endl;
    
    catch (...)
    
        std::cout << "Error";
    
    return 0;

【讨论】:

为什么grow_size是这样的? @v..snow。当一个向量的大小增加时,它通常会分配一个 2 倍于当前大小的新内存。所以这是我对文件扩展名数量的优化。 知道了。当我编辑我的问题时,似乎断言在额外字节> = 24时通过。我很不安,但这会起作用,但没有保证......

以上是关于boost::interprocess::managed_mapped_file 如何包含弹性向量?的主要内容,如果未能解决你的问题,请参考以下文章