Boost.Interprocess STL 兼容分配器提供 STL 兼容的分配器接口,如果它们定义其内部的 pointer 类型别名作为相对指针,则可用于将 STL 容器放置在共享内存、内存映射文件或用户定义的内存段中。
最初,在 C++98 和 C++03 中,这个 pointer 类型别名是无用的。正如 Scott Meyers 在他的《Effective STL》一书中第 10 条所提到的:“注意分配器的约定和限制”
显然,如果任何 STL 实现忽略 pointer 类型别名,则任何智能指针都不能用作 allocator::pointer。如果 STL 实现假定所有相同类型的分配器对象都相等比较,它将假定两个分配器,每个分配器从不同的内存池分配,是相等的,这是一个彻底的灾难。
我们希望用 Boost.Interprocess 放置在共享内存或内存映射文件中的 STL 容器无法做出任何这些假设,因此
这在理论上已在 C++11 中修复,当时引入了 std::pointer_traits 工具,并且标准库容器增加了对它们的 addSubview。然而,由于 ABI 问题和用户需求不高,一些实现仍然不支持 fancy pointer 类型,例如 boost::interprocess:offset_ptr。这些实现仍然在某些容器中使用原始指针进行内部数据或迭代器实现。
这种不便携的情况在 “P0773R0: Towards meaningful fancy pointers” 中有描述。
我们能否使用偏移的 fancy 指针类型来启用内存区域的安全共享?
是的;但对供应商的要求不明确,并且存在程序员的陷阱。
场景 (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 分配器隐式转换为分配其他类型的分配器的能力。
这里我们有一个示例,它构建了一个共享内存中的 map。键是字符串,映射类型是一个存储多个容器的类。
#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; }