共享内存分配> 2GB(需要链接到 VB6 使用的 32 位 DLL)



我在C++中使用来自 boost 库的共享内存,我正在尝试分配一个与其他进程共享的unordered_map。 服务器代码如下:

MapCreator.h

//#pragma once
#pragma warning( disable :4494 )
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/functional/hash.hpp>
#include <boost/unordered_map.hpp>
#include <iostream>
struct dataClass {
double Somma;
int Contatore;
};
namespace bip = boost::interprocess;
namespace bc = boost::container;
#ifdef COLIRU
using Segment = bip::managed_mapped_file;
#else
using Segment = bip::managed_shared_memory;
#endif
using Mgr = Segment::segment_manager;
template <typename T> using Alloc = bip::allocator<T, Mgr>;
using MyString = bc::basic_string<char, std::char_traits<char>, Alloc<char>>;
using KeyType = MyString;
using MappedType = dataClass;
using ValueType = std::pair<KeyType const, MappedType>;
using MySHMMap = boost::unordered_map<KeyType, MappedType, boost::hash<MyString>,
std::equal_to<MyString>, Alloc<ValueType>>;
class MapCreator {
public:
static constexpr int sizeDeclared = 1324*1024*1024; //< Here the problem, if i set 2000*1024*1024, the client application throw error
MapCreator(const char* Nome) // : nameMemory(Nome)
{

nameMemory = Nome;
remove();
segment = Segment{ bip::create_only, nameMemory, sizeDeclared };

mappa = segment.find_or_construct<MySHMMap>("MySHMMapName")(segment.get_segment_manager());
}
dataClass getValue(std::string key) const {
return mappa->at(MyString(key.c_str(), segment.get_segment_manager()));
}
void insertValue(std::string key,dataClass value) {
mappa->emplace(MyString(key.c_str(), segment.get_segment_manager()),
value);
}
double getFreeMemory() {
return ((double)segment.get_free_memory() / 1024 / 1024 / 1024);
}
long getSize() {
return mappa->size();
}
void remove() {
bip::shared_memory_object::remove(nameMemory);
}
double getTotalSize() {
return (double)sizeDeclared/1024/1024/1024;
}
double getTotalMemory() {
return (double)segment.get_size() / 1024 / 1024 / 1024;
}
private:

// note: declaration order defines initialization order!
const char* nameMemory = "SharedMemoryName";
Segment segment;//{ bip::open_or_create, nameMemory, sizeDeclared };
MySHMMap* mappa = nullptr;
};

同时主代码是这样的:

#include "MapCreator.h"
int main(){
MapCreator mappaClass("thread1");
mappaClass.insertValue("a", dataClass{ 3.12,2123 });
}

阅读器的代码如下:

ReaderFromMemory.h

//#pragma once
#pragma warning( disable :4494 )
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/functional/hash.hpp>
#include <boost/unordered_map.hpp>
#include <iostream>
struct dataClass {
double Somma;
int Contatore;
};
namespace bip = boost::interprocess;
namespace bc = boost::container;
#ifdef COLIRU
using Segment = bip::managed_mapped_file;
#else
using Segment = bip::managed_shared_memory;
#endif
using Mgr = Segment::segment_manager;
template <typename T> using Alloc = bip::allocator<T, Mgr>;
using MyString = bc::basic_string<char, std::char_traits<char>, Alloc<char>>;
using KeyType = MyString;
using MappedType = dataClass;
using ValueType = std::pair<KeyType const, MappedType>;
using MySHMMap = boost::unordered_map<KeyType, MappedType, boost::hash<MyString>,
std::equal_to<MyString>, Alloc<ValueType>>;
class Reader {
public:
Reader() : mappa(segment.find_or_construct<MySHMMap>("MySHMMapName")(
segment.get_segment_manager()))
{

}
dataClass getValue(const char* key) const {
return mappa->at(MyString(key, segment.get_segment_manager())); // < Here is the error while reading
}
private:
// note: declaration order defines initialization order!
static constexpr char const* nameMemory = "thread1";
Segment segment{ bip::open_only, nameMemory };;
MySHMMap* mappa = nullptr;
};

这是主要的:

#include "ReaderFromMemory.h"
int main(){
Reader reader;
auto testValue = reader.getValue("a");
}

所以问题是分配和读取超过 2GB。

我已经尝试使用/LARGEADDRESSAWARE 标志,但对于阅读器不起作用 我正在使用:

Visual Studio 2022 用于在 x86 模式下编译,因为阅读器是 VB6 使用的 DLL

提升库版本 1.78.0

由于这应该在 32 位环境中工作,因此您可以在直接访问您现在拥有的一张地图的顶部放置一个图层。以下是这个想法的大纲:

  1. 一张大地图分成尽可能多的小地图,以获得每个地图所需的大小远低于 32 位系统中可以解决的大小。例如<1 GB。
  2. 为密钥创建 64 位哈希函数。
  3. 使用哈希值的前 32 位选择正确的较小映射。如果您已将大地图拆分为 16 个较小的地图,则只需从该 32 位值中获取一个半字节(4 位)即可进行选择。
  4. 使用所选实际地图中哈希值的底部 32 位。
uint64_t hasher64(const KeyType& kt) {
//...
}
namespace std {
struct hash<KeyType> {
size_t operator()(const KeyType& kt) const {
return hasher64(kt) & 0xFFFFFFFF;
}
};
}
MySHMMap& getmap(const KeyType& kt) {
static MySHMMap maps[16];
return maps[(hasher64(kt) >> 32) & 0xF];
}
// ...
getmap(my_key)[my_key] = "foo";

上述方法对密钥进行了两次哈希处理,但会为您面临的限制提供解决方法。

您还可以使用一个单独的,便宜得多的功能来选择较小的地图。如果您的密钥std::string,可以通过查看字符串中的第一个字符来进行选择。

using KeyType = std::string;
MySHMMap& getmap(const KeyType& kt) {
static MySHMMap maps[16];
if(kt.empty()) return maps[0];
return maps[kt[0] & 0xF];
}

实现可以像这样开始(但绝不是完整的)。我使用了标准类型和容器,以免将其与增强细节弄乱,但这个想法应该可行。

#include <unordered_map>
#include <iostream>
template<class KeyType, class MappedType, size_t MapCo,
class MapSelector = std::hash<KeyType>>
class MapOverlay {
public:
using MySHMMap = std::unordered_map<KeyType, MappedType>;
using value_type = typename MySHMMap::value_type;
struct iterator { // an iterator to iterate seamlessly over the multiple maps
iterator(MapOverlay* mo, MySHMMap& m, typename MySHMMap::iterator c) :
mo(mo), map(&m), it(c) 
{
find_non_empty(); // find the first map with values
}
value_type& operator*() { return *it; }
value_type* operator->() { return &*it; }
iterator& operator++() {
if(it == map->end()) {
++map;
it = map->begin();
} else ++it;
find_non_empty();
return *this;
}
bool operator==(const iterator& rhs) const {
return map == rhs.map && it == rhs.it; 
}
bool operator!=(const iterator& rhs) const { return !(*this == rhs); }
private:
void find_non_empty() { // find the next map with values
while(it == map->end() && map != &mo->maps[MapCo-1]) {
++map;
it = map->begin();
}
}
MapOverlay* mo;
MySHMMap* map;
typename MySHMMap::iterator it;
}; // iterator end
// some member functions making it feel like a normal unordered_map
MappedType& operator[](const KeyType& kt) {
return map_lookup(kt)[kt];
}
iterator find(const KeyType& kt) {
auto& m = map_lookup(kt);
auto it = m.find(kt);
if(it == m.end()) return end();
return {this, m, it};
}
iterator begin() { return {this, maps[0], maps[0].begin()}; }
iterator end() { return {this, maps[MapCo-1], maps[MapCo-1].end()}; }
private:
MySHMMap& map_lookup(const KeyType& kt) {
// call the map selector for `kt` and use `% MapCo` to get it in range
return maps[ms(kt) % MapCo];  // ms is an instance of MapSelector
}
MySHMMap maps[MapCo];
MapSelector ms;
};

上述MapOverlay的默认设置是使用每个键上的MapSelectorstd::hash<KeyType>来选择地图。这可能比您需要的贵得多,因此您可以更快地提供一些替代方案。如果您使用不同的实现构建它,使用std::hash<KeyType>也有点风险。MapSelector算法共享地图的所有程序中必须相同。

// a simple class to do the underlying map selection cheaper than
// std::hash<std::string> will:
struct getmap {
size_t operator()(const std::string& kt) const {
// a naive, quick and simple map selection:
if(kt.empty()) return 0;
return kt[0];
}
};
int main() {
MapOverlay<std::string, std::string, 16, getmap> mo; // 16 small maps
mo["apa"] = "bepa"; // this will be stored in one map
mo["foo"] = "bar";  // and this in another
for(auto&[k, v] : mo) std::cout << k << " = " << v << 'n';
auto it = mo.find("foo");
if(it != mo.end()) std::cout << it->first << " = " << it->second << 'n';
}

演示

最新更新