Boost.Interprocess STL 兼容分配器提供了一个 STL 兼容的分配器接口,如果它们定义了自己的内部 pointer 类型别名作为相对指针,则可用于将 STL 容器放置在共享内存、内存映射文件或用户定义的内存段中。
最初,在 C++98 和 C++03 中,这个 pointer 类型别名是无用的。正如 Scott Meyers 在他的《Effective STL》一书的第 10 条中所提到的,“注意分配器的约定和限制”
显然,如果任何 STL 实现忽略指针类型别名,则任何智能指针都不能用作 allocator::pointer。如果 STL 实现假定同一类型的所有分配器对象都相等,它将假定每个从不同内存池分配的两个分配器是相等的,这会带来完全的灾难。
我们希望使用 Boost.Interprocess 放置在共享内存或内存映射文件中的 STL 容器不能做出这些假设,因此
理论上,这在 C++11 中得到了修复,当时引入了 std::pointer_traits
工具,并且标准库容器添加了对它们的支持。然而,由于 ABI 方面的考虑和用户需求的低下,一些实现仍然不支持 花式指针 类型,例如 boost::interprocess:offset_ptr
。这些实现仍然在某些容器中使用原始指针来实现内部数据或迭代器。
这种不可移植的情况在 “P0773R0: Towards meaningful fancy pointers” 中有描述。
我们能否使用偏移花式指针类型来启用内存区域的安全共享?
可以;但对供应商的要求不明确,并且对程序员存在陷阱。
方案 (A),偏移指针,只有一个可行的解决方案,“继续部分支持”。
![]() |
重要提示 |
---|---|
自 2011 年 Boost.Container 从 Boost.Interprocess 创建以来,该库一直维护了几个 <boost/interprocess/containers/.hpp> 头文件以保持向后兼容性。这些头文件现在已弃用,将在未来的 Boost 版本中删除。用户应直接使用 Boost.Container 头文件。 |
由于描述的标准库实现支持不完整,Boost.Interprocess 强烈建议使用 Boost.Container 容器,这些容器保证与 Boost.Interprocess 协同工作。
以下 Boost.Container 容器与 Boost.Interprocess 兼容:
deque
devector
flat_map/multimap
flat_set/multiset
list
map/multimap
set/multiset
slist
small_vector
stable_vector
static_vector
字符串
vector
要将任何这些容器放置在托管内存段中,我们必须使用 Boost.Interprocess 分配器来定义分配器模板参数,以便容器在托管内存段中分配值。要将容器本身放置在共享内存中,我们只需像使用 Boost.Interprocess 处理其他任何对象一样,在托管内存段中构造它。
#include <boost/container/vector.hpp> #include <boost/interprocess/allocators/allocator.hpp> #include <boost/interprocess/managed_shared_memory.hpp> int main () { using namespace boost::interprocess; //Remove shared memory on construction and destruction struct shm_remove { shm_remove() { shared_memory_object::remove("MyName"); } ~shm_remove(){ shared_memory_object::remove("MyName"); } } remover; //A managed shared memory where we can construct objects //associated with a c-string managed_shared_memory segment(create_only,"MyName", 65536); //Alias an STL-like allocator of ints that allocates ints from the segment typedef allocator<int, managed_shared_memory::segment_manager> ShmemAllocator; //Alias a vector that uses the previous STL-like allocator typedef boost::container::vector<int, ShmemAllocator> MyVector; int initVal[] = {0, 1, 2, 3, 4, 5, 6 }; const int *begVal = initVal; const int *endVal = initVal + sizeof(initVal)/sizeof(initVal[0]); //Initialize the STL-like allocator const ShmemAllocator alloc_inst (segment.get_segment_manager()); //Construct the vector in the shared memory segment with the STL-like allocator //from a range of iterators MyVector *myvector = segment.construct<MyVector> ("MyVector")/*object name*/ (begVal /*first ctor parameter*/ ,endVal /*second ctor parameter*/ ,alloc_inst /*third ctor parameter*/); //Use vector as your want std::sort(myvector->rbegin(), myvector->rend()); // . . . //When done, destroy and delete vector from the segment segment.destroy<MyVector>("MyVector"); return 0; }
这些容器还展示了如何轻松创建/修改现有容器,从而可以将其放置在共享内存中。
Boost.Interprocess 容器通过两种机制 同时 放置在共享内存/内存映射文件中等位置:
construct<>
、find_or_construct<>
…… 函数。这些函数将 C++ 对象放置在共享内存/内存映射文件中。但这只放置了对象本身,没有 放置该对象可能动态分配的内存。这意味着,要将任何 Boost.Interprocess 容器(包括 Boost.Interprocess 字符串)放置在共享内存或内存映射文件中,容器 必须:
如果您完成了前两点,但未使用 construct<>
或 find_or_construct<>
,那么您创建的容器将 仅 放置在您的进程中,但会从共享内存/内存映射文件中为包含的类型分配内存。
让我们看一个例子。
#include <boost/interprocess/managed_shared_memory.hpp> #include <boost/container/vector.hpp> #include <boost/container/string.hpp> #include <boost/interprocess/allocators/allocator.hpp> int main () { using namespace boost::interprocess; //Typedefs typedef allocator<char, managed_shared_memory::segment_manager> CharAllocator; typedef boost::container::basic_string<char, std::char_traits<char>, CharAllocator> MyShmString; typedef allocator<MyShmString, managed_shared_memory::segment_manager> StringAllocator; typedef boost::container::vector<MyShmString, StringAllocator> MyShmStringVector; //Open shared memory //Remove shared memory on construction and destruction struct shm_remove { shm_remove() { shared_memory_object::remove("MyName"); } ~shm_remove(){ shared_memory_object::remove("MyName"); } } remover; managed_shared_memory shm(create_only, "MyName", 10000); //Create allocators CharAllocator charallocator (shm.get_segment_manager()); StringAllocator stringallocator(shm.get_segment_manager()); //This string is in only in this process (the pointer pointing to the //buffer that will hold the text is not in shared memory). //But the buffer that will hold "this is my text" is allocated from //shared memory MyShmString mystring(charallocator); mystring = "this is my text"; //This vector is only in this process (the pointer pointing to the //buffer that will hold the MyShmString-s is not in shared memory). //But the buffer that will hold 10 MyShmString-s is allocated from //shared memory using StringAllocator. Since strings use a shared //memory allocator (CharAllocator) the 10 buffers that hold //"this is my text" text are also in shared memory. MyShmStringVector myvector(stringallocator); myvector.insert(myvector.begin(), 10, mystring); //This vector is fully constructed in shared memory. All pointers //buffers are constructed in the same shared memory segment //This vector can be safely accessed from other processes. MyShmStringVector *myshmvector = shm.construct<MyShmStringVector>("myshmvector")(stringallocator); myshmvector->insert(myshmvector->begin(), 10, mystring); //Destroy vector. This will free all strings that the vector contains shm.destroy_ptr(myshmvector); return 0; }
创建容器中的容器时,每个容器都需要一个分配器。为了避免使用具有复杂类型定义的多个分配器,我们可以利用 void 分配器提供的类型擦除能力,以及 void 分配器隐式转换为分配其他类型的分配器的能力。
这里有一个在共享内存中构建映射的示例。键是字符串,映射类型是一个存储多个容器的类。
#include <boost/interprocess/managed_shared_memory.hpp> #include <boost/interprocess/allocators/allocator.hpp> #include <boost/container/map.hpp> #include <boost/container/vector.hpp> #include <boost/container/string.hpp> using namespace boost::interprocess; //Typedefs of allocators and containers typedef managed_shared_memory::segment_manager segment_manager_t; typedef allocator<void, segment_manager_t> void_allocator; typedef allocator<int, segment_manager_t> int_allocator; typedef boost::container::vector<int, int_allocator> int_vector; typedef allocator<int_vector, segment_manager_t> int_vector_allocator; typedef boost::container::vector<int_vector, int_vector_allocator> int_vector_vector; typedef allocator<char, segment_manager_t> char_allocator; typedef boost::container::basic_string<char, std::char_traits<char>, char_allocator> char_string; class complex_data { int id_; char_string char_string_; int_vector_vector int_vector_vector_; public: //Since void_allocator is convertible to any other allocator<T>, we can simplify //the initialization taking just one allocator for all inner containers. complex_data(int id, const char *name, const void_allocator &void_alloc) : id_(id), char_string_(name, void_alloc), int_vector_vector_(void_alloc) {} //Other members... }; //Definition of the map holding a string as key and complex_data as mapped type typedef std::pair<const char_string, complex_data> map_value_type; typedef std::pair<char_string, complex_data> movable_to_map_value_type; typedef allocator<map_value_type, segment_manager_t> map_value_type_allocator; typedef boost::container::map< char_string, complex_data , std::less<char_string>, map_value_type_allocator> complex_map_type; int main () { //Remove shared memory on construction and destruction struct shm_remove { shm_remove() { shared_memory_object::remove("MyName"); } ~shm_remove(){ shared_memory_object::remove("MyName"); } } remover; //Create shared memory managed_shared_memory segment(create_only,"MyName", 65536); //An allocator convertible to any allocator<T, segment_manager_t> type void_allocator alloc_inst (segment.get_segment_manager()); //Construct the shared memory map and fill it complex_map_type *mymap = segment.construct<complex_map_type> //(object name), (first ctor parameter, second ctor parameter) ("MyMap")(std::less<char_string>(), alloc_inst); for(int i = 0; i < 100; ++i){ //Both key(string) and value(complex_data) need an allocator in their constructors char_string key_object(alloc_inst); complex_data mapped_object(i, "default_name", alloc_inst); map_value_type value(key_object, mapped_object); //Modify values and insert them in the map mymap->insert(value); } return 0; }