提高 IPC managed_memory_segment bad_alloc

Boost IPC managed_memory_segment bad_alloc

在尝试写入一段使用 shared_memory_segment 以写入“共享内存”的(可移植)C++ 代码时,我多次遇到 boost::interprocess::bad_alloc。来自 Boost documentation:

This exception is thrown when a memory request can't be fulfilled.

所以,我一定是分配的内存太少了。下面是代码(仅用于编写,因为此处阅读无关紧要):

shared_memory.h:

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/containers/string.hpp>

#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>

#include <string>
#include <exception>

namespace my_shared_memory
{
  typedef boost::interprocess::allocator<char, boost::interprocess::managed_shared_memory::segment_manager> CharAllocator;
  typedef boost::interprocess::basic_string<char, std::char_traits<char>, CharAllocator> IPCString;
  typedef boost::interprocess::allocator<IPCString, boost::interprocess::managed_shared_memory::segment_manager> StringAllocator;
  typedef boost::interprocess::vector<IPCString, StringAllocator> ShmVector;

  bool write_to_memory(std::string wsuid, std::string loop_val, std::string should_intercept, std::string post_data) ;

  const std::string shm_prefix = "shm_";
  const std::string mutex_prefix = "mtx_";
}

shared_memory.cpp:

#include "shared_memory.h"

namespace apl_shared_memory
{

  bool write_to_memory(std::string wsuid, std::string loop_val, std::string should_intercept, std::string post_data)
  {
    bool ret_val;
    std::string shm_name = shm_prefix + wsuid;
    std::string mtx_name = mutex_prefix + wsuid;
    boost::interprocess::named_mutex named_mtx{boost::interprocess::open_or_create, mtx_name.c_str()};
    size_t size = (sizeof(loop_val) + loop_val.size() + sizeof(should_intercept) + should_intercept.size() + sizeof post_data + post_data.size()) * 5;

    try
    {
      named_mtx.lock();
      boost::interprocess::shared_memory_object::remove(shm_name.c_str());
      boost::interprocess::managed_shared_memory segment(boost::interprocess::create_only, shm_name.c_str(), size);
      CharAllocator     charallocator  (segment.get_segment_manager());
      StringAllocator   stringallocator(segment.get_segment_manager());

      IPCString shm_loop_val(charallocator);
      IPCString shm_should_intercept(charallocator);
      IPCString shm_intercepted_data(charallocator);
      shm_loop_val = loop_val.c_str();
      shm_should_intercept = should_intercept.c_str();
      shm_intercepted_data = post_data.c_str();

      segment.destroy<ShmVector>("ShmVector");
      ShmVector *shmVector = segment.construct<ShmVector>("ShmVector")(stringallocator);
      shmVector->clear();
      shmVector->push_back(shm_loop_val);
      shmVector->push_back(shm_should_intercept);
      shmVector->push_back(shm_intercepted_data);

      named_mtx.unlock();
      ret_val = true;
    } catch(const std::exception& ex)
    {
      ret_val = false;
      named_mtx.unlock();
      boost::interprocess::shared_memory_object::remove(shm_name.c_str());
    }

    named_mtx.unlock();
    return ret_val;
  }
  
}

是的,我知道我不需要 unlock 在所有三个地方打电话。

问题似乎在行中:

size_t size = (sizeof(loop_val) + loop_val.size() + sizeof(should_intercept) + should_intercept.size() + sizeof post_data + post_data.size()) * 5;

我认为添加时间 5 太过分了,但事实显然并非如此。此代码在带有 gcc 6.3.0 的 Debian 9 和带有 Microsoft Visual Studio 2015 Community 的 Windows 10 上运行良好。但是,在具有 Xcode 10.1 的 MacOS 上,当我尝试在向量中插入第一个元素时,我得到 boost::interprocess::bad_alloc。该解决方案似乎将大小乘以 10 而不是 5,但这看起来很浪费。

我添加了 segment.get_free_memory 调用并确定对于包含以下三个字符串(不带引号)的向量

"true" "true" ""

我需要 512 个字节 Xcode。 sizeof 运算符分别为 IPCStringstd::stringShmVector 返回 32、24 和 32。因此,似乎我需要 32+4 字节来表示一个“真”字符串,而 32 来表示空字符串。在那里添加 ShmVector,我需要 36+36+32+32=136 字节用于结构本身。我使用 sizeof(std::string) 计算了大小,这里是 24(疏忽),所以我得到 (28+28+24)*5 = 400,这在这里还不够。

我这里的问题是如何确定段需要多少内存?我想写入段的数据在调用函数时是已知的,因此是它的大小。

编辑: 我将大小更改为:

size_t size = (sizeof(IPCString) + loop_val.size() + sizeof(IPCString) + should_intercept.size() + sizeof(IPCString) + post_data.size() + sizeof(ShmVector)) * 2 + 500;

到目前为止,还不错。在我完成对段的写入后,我的空闲 space 字节总是少于 500 字节。我尝试过各种数据大小,从不到 100 字节到 3 兆字节不等。我试过不乘以 2 但是,在那种情况下,当我尝试插入大数据块时,程序崩溃,因为 500 额外的字节没有提供足够的回旋余地。

如果几天后没有答案,我会post将此编辑作为答案。

您对字体大小的等效性做了很多假设。 c 字符串的内存使用量与 std::string 的内存使用量不同,后者与实际上是 vector 的 IPCString 的内存使用量不同,因此您不感兴趣尺寸或长度,但容量。

从马口中确定组件的大小:使用 IPCString 变量的 sizeof 而不是 std::string 变量,同时使用 shm_loop_val.capacity() + shm_should_intercept.capacity() + shm_intercepted_data.capacity() 而不是调整大小 std::strings!

你应该更接近:

size_t size = (3 * sizeof(IPCString) + sizeof(ShmVector) + shm_loop_val.capacity() +
              shm_should_intercept.capacity() + shm_intercepted_data.capacity();

Boost 实现也可能对您的数据进行字对齐,但我开始意识到这不太可能。这看起来像是连载的。我们可以调查您是否仍有问题。

经过一些研究和测试,我想出了以下解决方案。这里的重点是使用boost::move(或者,可能是std::move,不知道这里会不会有区别)。因为我们使用 boost::move,所以不会有 IPCString 对象的副本,因此不需要乘以 2。请记住,由于我们如何定义 IPCString,它将在共享内存段内分配。

增加的开销是必要的(可能是因为对齐或添加的一些其他开销或两者兼而有之)但总有一些 space 剩余。因为我可能碰巧发送几兆字节,所以 500 字节的开销非常小。

我正在使用 std::stringsize() 方法,因为它 returns the size of the string in bytes.

代码如下:

shared_memory.h:

#pragma once

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/containers/string.hpp>

#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>

#include <string>
#include <vector>
#include <exception>

namespace apl_shared_memory
{
  typedef boost::interprocess::allocator<char, boost::interprocess::managed_shared_memory::segment_manager> CharAllocator;
  typedef boost::interprocess::basic_string<char, std::char_traits<char>, CharAllocator> IPCString;
  typedef boost::interprocess::allocator<IPCString, boost::interprocess::managed_shared_memory::segment_manager> StringAllocator;
  typedef boost::interprocess::vector<IPCString, StringAllocator> ShmVector;

  bool write_to_memory(std::string wsuid, std::string loop_val, std::string should_intercept, std::string post_data) ;


  const std::string shm_prefix = "shm_";
  const std::string mutex_prefix = "mtx_";
}

shared_memory.cpp:

#include "shared_memory.h"

#include "logger.h"



namespace apl_shared_memory

{
    bool write_to_memory(std::string wsuid, std::string loop_val, std::string should_intercept, std::string post_data)
    {
        bool ret_val;
        std::string shm_name = shm_prefix + wsuid;
        std::string mtx_name = mutex_prefix + wsuid;

        boost::interprocess::named_mutex named_mtx{ boost::interprocess::open_or_create, mtx_name.c_str() };

        // Add the size of all the structures we're putting in the shared memory region and add some for the overhead.
        size_t size = (3*sizeof(IPCString) + loop_val.size() + should_intercept.size() + post_data.size() + sizeof(ShmVector)) + 500;

        try
        {
            named_mtx.lock();
            boost::interprocess::shared_memory_object::remove(shm_name.c_str());
            boost::interprocess::managed_shared_memory segment(boost::interprocess::create_only, shm_name.c_str(), size);

            CharAllocator     charallocator(segment.get_segment_manager());
            StringAllocator   stringallocator(segment.get_segment_manager());

            IPCString shm_loop_val(charallocator);
            IPCString shm_should_intercept(charallocator);
            IPCString shm_intercepted_data(charallocator);

            shm_loop_val = loop_val.c_str();
            shm_should_intercept = should_intercept.c_str();
            shm_intercepted_data = post_data.c_str();

            segment.destroy<ShmVector>("ShmVector");
            ShmVector *shmVector = segment.construct<ShmVector>("ShmVector")(stringallocator);
            shmVector->clear();
            shmVector->reserve(3);

            // push_back will call a copy-constructor. But, if we send a rvalue reference (i.e. if we move it), there will be no copying.
            shmVector->push_back(boost::move(shm_loop_val));
            shmVector->push_back(boost::move(shm_should_intercept));
            shmVector->push_back(boost::move(shm_intercepted_data));

            ret_val = true;
        }

        catch (const std::exception& ex)

        {
            ret_val = false;
            boost::interprocess::shared_memory_object::remove(shm_name.c_str());
        }

        named_mtx.unlock();

        return ret_val;
    }
}

如果有人认为我做错了什么,请评论。