提升 IPC managed_memory_segment bad_alloc
Posted
技术标签:
【中文标题】提升 IPC managed_memory_segment bad_alloc【英文标题】:Boost IPC managed_memory_segment bad_alloc 【发布时间】:2019-01-18 14:56:14 【问题描述】:在尝试写入一段使用shared_memory_segment
以写入“共享内存”的(便携式)C++ 代码时,我多次遇到boost::interprocess::bad_alloc
。来自Boost documentation:
当无法满足内存请求时会引发此异常。
所以,我一定是分配的内存太少了。下面是代码(仅供写作,因为在这里阅读无关紧要):
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_mtxboost::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
调用并确定对于包含以下三个字符串(不带引号)的向量
“真”“真”“”
我需要 512 字节的 Xcode。 sizeof
运算符分别为 IPCString
、std::string
和 ShmVector
返回 32、24 和 32。因此,似乎我需要32+4
字节来表示一个“真”字符串,而32
则需要32
来表示空字符串。在那里添加 ShmVector,我需要 36+36+32+32=136
字节作为结构本身。我使用sizeof(std::string)
计算了大小,这里是 24(疏忽),所以给了我(28+28+24)*5 = 400
,这还不够。
我的问题是如何确定段需要多少内存?我要写入段的数据在函数被调用时是已知的,因此是它的大小。 p>
编辑:我将尺寸改为:
size_t size = (sizeof(IPCString) + loop_val.size() + sizeof(IPCString) + should_intercept.size() + sizeof(IPCString) + post_data.size() + sizeof(ShmVector)) * 2 + 500;
到目前为止,一切都很好。在我完成对段的写入后,我的可用空间总是少于500
字节。我尝试过各种数据大小,从小于 100 字节到 3 兆字节不等。我试过不乘以2
,但在这种情况下,当我尝试插入大数据块时,程序会崩溃,因为500
额外的字节没有提供足够的余地。
如果几天后没有答案,我将发布此编辑作为答案。
【问题讨论】:
您的计算中似乎没有考虑对齐。 我想这部分是由于这个。关于如何考虑这一点的任何提示? 最简单的方法是sizeof
一个你声明的结构,它包含你正在评估的所有东西的成员。编译器将为对齐添加填充。
我可以尝试一下,但我主要担心 Boost 会在我插入的数据结构之外添加额外的开销,甚至在我插入任何内容之前。
不会有任何开销。 sizeof 在编译时评估。尽管您需要了解您正在使用的 boost 模块的实现,才能真正理解如何编写此代码。或者在他们的文档中找到一个相关的例子。看到直接处理共享内存的高度模板化的代码真的很奇怪。
【参考方案1】:
您对字体大小的等效性做了很多假设。 c 字符串的内存使用量与 std::string 的内存使用量不同,后者与 IPCString 的内存使用量不同,IPCString 实际上是vector,因此您对大小或长度不感兴趣,但容量。
从马的嘴里调整你的组件大小:取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 实现也可能会对齐您的数据,但我开始意识到这不太可能。这看起来像是序列化的。我们可以调查您是否仍有问题。
【讨论】:
我相信我们正在传递结构的元素的分配器。在IPCString
的情况下,元素是“字符”,在ShmVector
的情况下,元素是IPCString
s。我的想法来自:***.com/questions/12980716/…
@nm_tp 我明白了,它代表进程间 CString。我认为 IPC 是前缀,这些是字符串对象。在首字母缩写词后使用蛇形大小写很好,所以IP_CString。
实际上是IPC_String
。 IPC
在进程间通信中。这些天我将发布一个没有任何 bad_alloc 异常的答案。
@nm_tp 很高兴你自己想出来了。
另外,关于capacity()
的方法,我真的不能用。我需要size
来打开并为段分配内存,但我需要IPCString
的实例来确定大小。我对sizeof IPCString + loop_val.size()
的运气更好,因为size()
返回字符串的长度以字节为单位。【参考方案2】:
经过一些研究和测试,我想出了下面的解决方案。这里的重点是使用boost::move
(或者,也许是std::move,不知道这里会不会有区别)。因为我们使用的是boost::move
,所以不会有IPCString
对象的副本,因此不需要乘以2
。请记住,由于我们如何定义IPCString
,它将在共享内存段内分配。
增加开销是必要的(可能是因为对齐或添加了其他一些开销或两者兼而有之),但总是有一些空间。因为我可能碰巧发送了几兆字节,500
字节的开销非常小。
我正在使用std::string
的size()
方法,因为它返回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;
如果有人认为我做错了什么,请发表评论。
【讨论】:
IPCString 的内存使用量不是 c 字符串等效的字节大小。 想详细说明一下?我看到您对此非常投入,但是到目前为止,您还没有给我任何可用的解决方案和/或提示。你只是告诉我出了点问题,但你没有提供任何解释。typedef boost::interprocess::basic_string<char, std::char_traits<char>, CharAllocator> IPCString;
这行表示 basic_string 的元素是 char
s。 size()
方法返回 元素的数量 但 sizeof(char)
是 1 所以,在这种情况下,我可以使用 std::string
的 size()
来确定我需要的字节数,不包括开销。我看不出为什么容量会是我刚刚插入IPCString
的缓冲区大小的两倍。以上是关于提升 IPC managed_memory_segment bad_alloc的主要内容,如果未能解决你的问题,请参考以下文章