![]() |
![]() ![]() ![]() ![]() ![]() |
![]() |
Managed Memory SegmentsBoost , The Boost C++ Libraries BoostBook Documentation Subset , Chapter 16. Boost.Interprocess
|
![]() | Caution |
---|---|
Эта функция экспериментальная, интерфейс и ABI нестабильны. |
Если приложение должно выделять много буферов памяти, но оно должно самостоятельно распределять их, приложение обычно вынуждено запускать вызов<allocate()
>. Сегменты управляемой памяти предлагают альтернативную функцию для пакетирования нескольких выделений в один вызов, получая буферы памяти, которые:
Этот метод распределения намного быстрее, чем вызов<allocate()
>в петле. Недостатком является то, что сегмент должен обеспечивать смежный сегмент памяти, достаточно большой, чтобы удерживать все выделения. Сегменты управляемой памяти обеспечивают эту функциональность с помощью функций<allocate_many()
>. Существует 2 типа функций<allocate_many
>:
//!Allocates n_elements of elem_bytes bytes. //!Throws bad_alloc on failure. chain.size() is not increased on failure. void allocate_many(size_type elem_bytes, size_type n_elements, multiallocation_chain &chain); //!Allocates n_elements, each one of element_lengths[i]*sizeof_element bytes. //!Throws bad_alloc on failure. chain.size() is not increased on failure. void allocate_many(const size_type *element_lengths, size_type n_elements, size_type sizeof_element, multiallocation_chain &chain); //!Allocates n_elements of elem_bytes bytes. //!Non-throwing version. chain.size() is not increased on failure. void allocate_many(std::nothrow_t, size_type elem_bytes, size_type n_elements, multiallocation_chain &chain); //!Allocates n_elements, each one of //!element_lengths[i]*sizeof_element bytes. //!Non-throwing version. chain.size() is not increased on failure. void allocate_many(std::nothrow_t, const size_type *elem_sizes, size_type n_elements, size_type sizeof_element, multiallocation_chain &chain); //!Deallocates all elements contained in chain. //!Never throws. void deallocate_many(multiallocation_chain &chain);
Вот небольшой пример, показывающий всю эту функциональность:
#include <boost/interprocess/managed_shared_memory.hpp> #include <boost/move/utility_core.hpp> //boost::move #include <cassert>//assert #include <cstring>//std::memset #include <new> //std::nothrow #include <vector> //std::vector int main() { using namespace boost::interprocess; typedef managed_shared_memory::multiallocation_chain multiallocation_chain; //Remove shared memory on construction and destruction struct shm_remove { shm_remove() { shared_memory_object::remove("MySharedMemory"); } ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); } } remover; managed_shared_memory managed_shm(create_only,"MySharedMemory", 65536); //Allocate 16 elements of 100 bytes in a single call. Non-throwing version. multiallocation_chain chain; managed_shm.allocate_many(std::nothrow, 100, 16, chain); //Check if the memory allocation was successful if(chain.empty()) return 1; //Allocated buffers std::vector<void*> allocated_buffers; //Initialize our data while(!chain.empty()){ void *buf = chain.pop_front(); allocated_buffers.push_back(buf); //The iterator must be incremented before overwriting memory //because otherwise, the iterator is invalidated. std::memset(buf, 0, 100); } //Now deallocate while(!allocated_buffers.empty()){ managed_shm.deallocate(allocated_buffers.back()); allocated_buffers.pop_back(); } //Allocate 10 buffers of different sizes in a single call. Throwing version managed_shared_memory::size_type sizes[10]; for(std::size_t i = 0; i < 10; ++i) sizes[i] = i*3; managed_shm.allocate_many(sizes, 10, 1, chain); managed_shm.deallocate_many(chain); return 0; }
Выделение N-буферов одинакового размера улучшает производительность пулов и контейнеров узлов (например, STL-подобных списков): при вставке ряда передних итераторов в STL-подобный список функция вставки может обнаружить количество необходимых элементов и выделить в один вызов. Узлы все еще могут быть размещены.
Выделение N буферов разных размеров может быть использовано для ускорения распределения в тех случаях, когда несколько объектов всегда должны быть выделены одновременно, но размещены в разное время. Например, класс может выполнять несколько начальных распределений (некоторые данные заголовка для сетевого пакета, например) в своем конструкторе, но также и распределения буферов, которые могут быть перераспределены в будущем (данные, которые будут отправлены через сеть). Вместо того, чтобы распределять все данные независимо, конструктор может использовать<allocate_many()
>для ускорения инициализации, но он все еще может распределять и расширять память элемента переменного размера.
В общем случае<allocate_many
>полезно при больших значениях N. Чрезмерное использование<allocate_many
>может увеличить эффективное использование памяти, поскольку оно не может повторно использовать существующие фрагменты памяти, которые могут быть доступны для некоторых элементов.
При программировании некоторых структур данных, таких как векторы, перераспределение памяти становится важным инструментом для повышения производительности. Сегменты управляемой памяти предлагают расширенную функцию перераспределения, которая предлагает:
Расширение может быть объединено с выделением нового буфера, если расширение не получает функцию с семантикой «расширять, если не выделять новый буфер».
Помимо этих функций, функция всегда возвращает реальный размер выделенного буфера, потому что много раз из-за проблем с выравниванием выделенный буфер немного больше запрашиваемого размера. Таким образом, программист может максимизировать использование памяти, используя<allocation_command
>.
Вот декларация функции:
enum boost::interprocess::allocation_type { //Bitwise OR (|) combinable values boost::interprocess::allocate_new = ..., boost::interprocess::expand_fwd = ..., boost::interprocess::expand_bwd = ..., boost::interprocess::shrink_in_place = ..., boost::interprocess::nothrow_allocation = ... }; template<class T> std::pair<T *, bool> allocation_command( boost::interprocess::allocation_type command , std::size_t limit_size , size_type &prefer_in_recvd_out_size , T *&reuse_ptr);
Предпосылки для функции:
boost::interprocess::shrink_in_place
>, она не может содержать ни одного из этих значений:<boost::interprocess::expand_fwd
>,<boost::interprocess::expand_bwd
>.boost::interprocess::expand_fwd
>или<boost::interprocess::expand_bwd
>, параметр<reuse_ptr
>должен быть ненулевым и возвращен предыдущей функцией распределения.boost::interprocess::shrink_in_place
>, параметр<limit_size
>должен быть равен или больше параметра<preferred_size
>.command
>содержит любое из этих значений:<boost::interprocess::expand_fwd
>или<boost::interprocess::expand_bwd
>, параметр<limit_size
>должен быть равен или меньше параметра<preferred_size
>.Каковы последствия этой функции:
boost::interprocess::shrink_in_place
>, функция попытается уменьшить размер блока памяти, на который ссылается указатель<reuse_ptr
>, до значения<preferred_size
>, перемещающего только конец блока. Если это невозможно, он будет пытаться уменьшить размер блока памяти настолько, насколько это возможно, пока это приводит к<size(p)<=limit_size
>. Об успехе сообщается только в том случае, если это приводит к<preferred_size
<=size(p)
>и<size(p)<=limit_size
>.command
>содержит только значение<boost::interprocess::expand_fwd
>(с дополнительным дополнительным<boost::interprocess::nothrow_allocation
>), распределитель попытается увеличить размер блока памяти, на который ссылаются указатели, повторно перемещая только конец блока к значению<preferred_size
>. Если это невозможно, он будет пытаться увеличить размер блока памяти настолько, насколько это возможно, пока это приводит к<size(p)>=limit_size
>. Об успехе сообщается только в том случае, если это приводит к<limit_size
<=size(p)
>.command
>содержит только значение<boost::interprocess::expand_bwd
>(с дополнительным дополнительным<boost::interprocess::nothrow_allocation
>), распределитель будет пытаться увеличить размер блока памяти, на который ссылается указатель<reuse_ptr
>, только перемещая начало блока в возвращенное новое положение<new_ptr
>. Если это невозможно, он будет пытаться переместить начало блока как можно дольше, пока это приводит к<size(new_ptr)>=limit_size
>. Об успехе сообщается только в том случае, если это приводит к<limit_size
<=size(new_ptr)
>.command
>содержит только значение<boost::interprocess::allocate_new
>(с дополнительным дополнительным<boost::interprocess::nothrow_allocation
>), то распределитель попытается выделить память для<preferred_size
>объектов. Если это невозможно, он попытается выделить память как минимум на 318 объектов.command
>содержит только комбинацию<boost::interprocess::expand_fwd
>и<boost::interprocess::allocate_new
>, (с дополнительным дополнительным<boost::interprocess::nothrow_allocation
>) распределитель сначала попробует прямое расширение. Если это не удастся, он попробует новое распределение.command
>содержит только комбинацию<boost::interprocess::expand_bwd
>и<boost::interprocess::allocate_new
>(с дополнительным дополнительным<boost::interprocess::nothrow_allocation
>), то распределитель постарается сначала получить<preferred_size
>объекты, используя оба метода, если это необходимо. Если это не удастся, он попытается получить<limit_size
>объектов, используя оба метода, если это необходимо.command
>содержит только комбинацию<boost::interprocess::expand_fwd
>и<boost::interprocess::expand_bwd
>(с дополнительным дополнительным<boost::interprocess::nothrow_allocation
>), распределитель сначала попробует прямое расширение. Если это не удается, он попытается получить объекты предпочтительного размера, используя обратное расширение или комбинацию прямого и обратного расширения. Если это не удастся, он попытается получить<limit_size
>объекты, используя оба метода, если это необходимо.command
>содержит только комбинацию распределений_new,<boost::interprocess::expand_fwd
>и<boost::interprocess::expand_bwd
>, (с дополнительным дополнительным<boost::interprocess::nothrow_allocation
>) распределитель сначала попробует прямое расширение. Если это не удается, он попытается получить объекты предпочтительного размера с использованием нового распределения, обратного расширения или комбинации прямого и обратного расширения. Если это не удастся, он попытается получить<limit_size
>объектов, используя те же методы.received_size
>. При неудаче распределитель записывает в<received_size
>возможный успешный<limit_size
>параметр для нового вызова.Исключение, если выполняются два условия:
boost::interprocess::nothrow_allocation
>.Эта функция возвращается:
boost::interprocess::nothrow_allocation
>, первый элемент будет равен 0, если распределение/расширение не удается или есть ошибка в предварительных условиях.Notes:
char
>в качестве аргумента шаблона, возвращенный буфер будет соответствующим образом выровнен для удержания любого типа.char
>в качестве аргумента шаблона и выполняется обратное расширение, хотя и правильно выровнено, возвращенный буфер может не подходить, потому что расстояние между новым началом и старым началом может не превышать тип, который пользователь хочет построить, поскольку из-за внутренних ограничений расширение может быть немного больше, чем запрошенные байты.При выполнении обратного расширения, если вы уже построили объекты в старом буфере, обязательно укажите правильно тип.Вот небольшой пример использования<allocation_command
>:
#include <boost/interprocess/managed_shared_memory.hpp> #include <cassert> int main() { using namespace boost::interprocess; //Remove shared memory on construction and destruction struct shm_remove { shm_remove() { shared_memory_object::remove("MySharedMemory"); } ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); } } remover; //Managed memory segment that allocates portions of a shared memory //segment with the default management algorithm managed_shared_memory managed_shm(create_only, "MySharedMemory", 10000*sizeof(std::size_t)); //Allocate at least 100 bytes, 1000 bytes if possible managed_shared_memory::size_type min_size = 100; managed_shared_memory::size_type first_received_size = 1000; std::size_t *hint = 0; std::size_t *ptr = managed_shm.allocation_command<std::size_t> (boost::interprocess::allocate_new, min_size, first_received_size, hint); //Received size must be bigger than min_size assert(first_received_size >= min_size); //Get free memory managed_shared_memory::size_type free_memory_after_allocation = managed_shm.get_free_memory(); //Now write the data for(std::size_t i = 0; i < first_received_size; ++i) ptr[i] = i; //Now try to triplicate the buffer. We won't admit an expansion //lower to the double of the original buffer. //This "should" be successful since no other class is allocating //memory from the segment min_size = first_received_size*2; managed_shared_memory::size_type expanded_size = first_received_size*3; std::size_t * ret = managed_shm.allocation_command (boost::interprocess::expand_fwd, min_size, expanded_size, ptr); //Check invariants assert(ptr != 0); assert(ret == ptr); assert(expanded_size >= first_received_size*2); //Get free memory and compare managed_shared_memory::size_type free_memory_after_expansion = managed_shm.get_free_memory(); assert(free_memory_after_expansion < free_memory_after_allocation); //Write new values for(std::size_t i = first_received_size; i < expanded_size; ++i) ptr[i] = i; //Try to shrink approximately to min_size, but the new size //should be smaller than min_size*2. //This "should" be successful since no other class is allocating //memory from the segment managed_shared_memory::size_type shrunk_size = min_size; ret = managed_shm.allocation_command (boost::interprocess::shrink_in_place, min_size*2, shrunk_size, ptr); //Check invariants assert(ptr != 0); assert(ret == ptr); assert(shrunk_size <= min_size*2); assert(shrunk_size >= min_size); //Get free memory and compare managed_shared_memory::size_type free_memory_after_shrinking = managed_shm.get_free_memory(); assert(free_memory_after_shrinking > free_memory_after_expansion); //Deallocate the buffer managed_shm.deallocate(ptr); return 0; }
<allocation_command
>— очень мощная функция, которая может привести к значительному повышению производительности. Это особенно полезно при программировании векторных структур данных, где программист может минимизировать как количество запросов на выделение, так и потери памяти.
При отображении сегмента памяти на основе совместно используемой памяти или файлов есть возможность открыть их с помощьюopen_copy_on_writeопции. Эта опция похожа на<open_only
>, но каждое изменение, которое программист делает с этим управляемым сегментом, остается закрытым для этого процесса и не переводится на базовое устройство (общая память или файл).
Базовая общая память или файл открываются только для чтения, поэтому несколько процессов могут совместно использовать начальный управляемый сегмент и вносить в него частные изменения. Если многие процессы открывают управляемый сегмент в режиме копирования в режиме записи, а не модифицированные страницы из управляемого сегмента будут совместно использоваться всеми этими процессами, при значительной экономии памяти.
Открытие управляемой совместно используемой памяти и отображенных файлов сopen_read_onlyотображает базовое устройство в памяти сатрибутами только для чтения. Это означает, что любая попытка написать эту память, создавая объекты или блокируя любой mutex, может привести к ошибке ошибки страницы (и, следовательно, прекращению программы) из ОС. Режим только для чтения открывает базовое устройство (общая память, файл ...) в режиме только для чтения и может привести к значительной экономии памяти, если несколько процессов просто хотят обработать управляемый сегмент памяти без его изменения. Режим работы только для чтения ограничен:
find<>
>не использует внутренние замки и может использоваться для поиска названных и уникальных объектов.Вот пример, который показывает использование этих двух открытых режимов:
#include <boost/interprocess/managed_mapped_file.hpp> #include <fstream> //std::fstream #include <iterator>//std::distance int main() { using namespace boost::interprocess; //Define file names const char *ManagedFile = "MyManagedFile"; const char *ManagedFile2 = "MyManagedFile2"; //Try to erase any previous managed segment with the same name file_mapping::remove(ManagedFile); file_mapping::remove(ManagedFile2); remove_file_on_destroy destroyer1(ManagedFile); remove_file_on_destroy destroyer2(ManagedFile2); { //Create an named integer in a managed mapped file managed_mapped_file managed_file(create_only, ManagedFile, 65536); managed_file.construct<int>("MyInt")(0u); //Now create a copy on write version managed_mapped_file managed_file_cow(open_copy_on_write, ManagedFile); //Erase the int and create a new one if(!managed_file_cow.destroy<int>("MyInt")) throw int(0); managed_file_cow.construct<int>("MyInt2"); //Check changes if(managed_file_cow.find<int>("MyInt").first && !managed_file_cow.find<int>("MyInt2").first) throw int(0); //Check the original is intact if(!managed_file.find<int>("MyInt").first && managed_file.find<int>("MyInt2").first) throw int(0); { //Dump the modified copy on write segment to a file std::fstream file(ManagedFile2, std::ios_base::out | std::ios_base::binary); if(!file) throw int(0); file.write(static_cast<const char *>(managed_file_cow.get_address()), (std::streamsize)managed_file_cow.get_size()); } //Now open the modified file and test changes managed_mapped_file managed_file_cow2(open_only, ManagedFile2); if(managed_file_cow2.find<int>("MyInt").first && !managed_file_cow2.find<int>("MyInt2").first) throw int(0); } { //Now create a read-only version managed_mapped_file managed_file_ro(open_read_only, ManagedFile); //Check the original is intact if(!managed_file_ro.find<int>("MyInt").first && managed_file_ro.find<int>("MyInt2").first) throw int(0); //Check the number of named objects using the iterators if(std::distance(managed_file_ro.named_begin(), managed_file_ro.named_end()) != 1 && std::distance(managed_file_ro.unique_begin(), managed_file_ro.unique_end()) != 0 ) throw int(0); } return 0; }
Boost.Interprocess offers managed shared
memory between processes using managed_shared_memory
or managed_mapped_file
. Two
processes just map the same the memory mappable resource and read from and
write to that object.
Много раз мы не хотим использовать этот подход совместной памяти, и мы предпочитаем отправлять сериализованные данные через сеть, локальные розетки или очереди сообщений. Сериализация может осуществляться черезBoost.Serializationили аналогичную библиотеку. Однако, если два процесса имеют один и тот же ABI (прикладной двоичный интерфейс), мы можем использовать одни и те же возможности построения объектов и контейнеров<managed_shared_memory
>или<managed_heap_memory
>для создания всей информации в одном буфере, который будет отправлен, например, через очереди сообщений. Приемник просто скопировал данные в локальный буфер, и он мог читать или изменять их напрямую, не десерализуя данные. Такой подход может быть гораздо эффективнее сложного механизма сериализации.
Приложения для сервисовBoost.Interprocessс использованием буферов необщей памяти:
Чтобы помочь с этим управлением,Boost.Interprocessпредоставляет два полезных класса,<basic_managed_heap_memory
>и<basic_managed_external_buffer
>:
Иногда пользователь хочет создать простые объекты, совместимые с STL контейнеры, совместимые с STL строки и многое другое, все в одном буфере. Этот буфер может быть большим статическим буфером, вспомогательным устройством с картой памяти или любым другим пользовательским буфером.
Это позволило бы упростить сериализацию, и нам просто нужно будет скопировать буфер для дублирования всех объектов, созданных в исходном буфере, включая сложные объекты, такие как карты, списки...Boost.Interprocessпредлагает классы сегментов управляемой памяти для обработки буферов, предоставляемых пользователем, которые обеспечивают ту же функциональность, что и классы общей памяти:
//Named object creation managed memory segment //All objects are constructed in a user provided buffer template < class CharType, class MemoryAlgorithm, template<class IndexConfig> class IndexType > class basic_managed_external_buffer; //Named object creation managed memory segment //All objects are constructed in a user provided buffer // Names are c-strings, // Default memory management algorithm // (rbtree_best_fit with no mutexes and relative pointers) // Name-object mappings are stored in the default index type (flat_map) typedef basic_managed_external_buffer < char, rbtree_best_fit<null_mutex_family, offset_ptr<void> >, flat_map_index > managed_external_buffer; //Named object creation managed memory segment //All objects are constructed in a user provided buffer // Names are wide-strings, // Default memory management algorithm // (rbtree_best_fit with no mutexes and relative pointers) // Name-object mappings are stored in the default index type (flat_map) typedef basic_managed_external_buffer< wchar_t, rbtree_best_fit<null_mutex_family, offset_ptr<void> >, flat_map_index > wmanaged_external_buffer;
Чтобы использовать управляемый внешний буфер, вы должны включить следующий заголовок:
#include <boost/interprocess/managed_external_buffer.hpp>
Рассмотрим пример использования управляемого_внешнего_буфера:
#include <boost/interprocess/managed_external_buffer.hpp> #include <boost/interprocess/allocators/allocator.hpp> #include <boost/interprocess/containers/list.hpp> #include <cstring> #include <boost/aligned_storage.hpp> int main() { using namespace boost::interprocess; //Create the static memory who will store all objects const int memsize = 65536; static boost::aligned_storage<memsize>::type static_buffer; //This managed memory will construct objects associated with //a wide string in the static buffer wmanaged_external_buffer objects_in_static_memory (create_only, &static_buffer, memsize); //We optimize resources to create 100 named objects in the static buffer objects_in_static_memory.reserve_named_objects(100); //Alias an integer node allocator type //This allocator will allocate memory inside the static buffer typedef allocator<int, wmanaged_external_buffer::segment_manager> allocator_t; //Alias a STL compatible list to be constructed in the static buffer typedef list<int, allocator_t> MyBufferList; //The list must be initialized with the allocator //All objects created with objects_in_static_memory will //be stored in the static_buffer! MyBufferList *list = objects_in_static_memory.construct<MyBufferList>(L"MyList") (objects_in_static_memory.get_segment_manager()); //Since the allocation algorithm from wmanaged_external_buffer uses relative //pointers and all the pointers constructed int the static memory point //to objects in the same segment, we can create another static buffer //from the first one and duplicate all the data. static boost::aligned_storage<memsize>::type static_buffer2; std::memcpy(&static_buffer2, &static_buffer, memsize); //Now open the duplicated managed memory passing the memory as argument wmanaged_external_buffer objects_in_static_memory2 (open_only, &static_buffer2, memsize); //Check that "MyList" has been duplicated in the second buffer if(!objects_in_static_memory2.find<MyBufferList>(L"MyList").first) return 1; //Destroy the lists from the static buffers objects_in_static_memory.destroy<MyBufferList>(L"MyList"); objects_in_static_memory2.destroy<MyBufferList>(L"MyList"); return 0; }
Boost.InterprocessSTL-совместимые распределители также могут использоваться для размещения STL-совместимых контейнеров в пользовательском сегменте.
<basic_managed_external_buffer
>также может быть полезным для создания небольших баз данных для встраиваемых систем, ограничивающих размер используемой памяти предопределенным объемом памяти, вместо того, чтобы позволить базе данных фрагментировать кучу памяти.
Использование кучной памяти (новой/удаленной) для получения буфера, где пользователь хочет хранить все свои данные, очень распространено, поэтомуBoost.Interprocessпредоставляет некоторые специализированные классы, которые работают исключительно с кучной памятью.
Это классы:
//Named object creation managed memory segment //All objects are constructed in a single buffer allocated via new[] template < class CharType, class MemoryAlgorithm, template<class IndexConfig> class IndexType > class basic_managed_heap_memory; //Named object creation managed memory segment //All objects are constructed in a single buffer allocated via new[] // Names are c-strings, // Default memory management algorithm // (rbtree_best_fit with no mutexes and relative pointers) // Name-object mappings are stored in the default index type (flat_map) typedef basic_managed_heap_memory < char, rbtree_best_fit<null_mutex_family>, flat_map_index > managed_heap_memory; //Named object creation managed memory segment //All objects are constructed in a single buffer allocated via new[] // Names are wide-strings, // Default memory management algorithm // (rbtree_best_fit with no mutexes and relative pointers) // Name-object mappings are stored in the default index type (flat_map) typedef basic_managed_heap_memory< wchar_t, rbtree_best_fit<null_mutex_family>, flat_map_index > wmanaged_heap_memory;
Чтобы использовать управляемую кучу памяти, вы должны включить следующий заголовок:
#include <boost/interprocess/managed_heap_memory.hpp>
Использование точно такое же, как<basic_managed_external_buffer
>, за исключением того, что память создается самим управляемым сегментом памяти с использованием динамической (новой/удаленной) памяти.
basic_managed_heap_memoryтакже предлагает функцию<grow(std::size_textra_bytes)
>, которая пытается изменить размер внутренней кучной памяти, чтобы у нас было место для большего количества объектов. Нобудьте осторожны, если память перераспределена, старый буфер будет скопирован в новый, так что все объекты будут двоичными копиями в новый буфер. Чтобы использовать эту функцию, все указатели, построенные в буфере кучи, которые указывают на объекты в буфере кучи, должны быть относительными указателями (например,<offset_ptr
>). В противном случае результат не определен. Вот пример:
#include <boost/interprocess/containers/list.hpp> #include <boost/interprocess/managed_heap_memory.hpp> #include <boost/interprocess/allocators/allocator.hpp> #include <cstddef> using namespace boost::interprocess; typedef list<int, allocator<int, managed_heap_memory::segment_manager> > MyList; int main () { //We will create a buffer of 1000 bytes to store a list managed_heap_memory heap_memory(1000); MyList * mylist = heap_memory.construct<MyList>("MyList") (heap_memory.get_segment_manager()); //Obtain handle, that identifies the list in the buffer managed_heap_memory::handle_t list_handle = heap_memory.get_handle_from_address(mylist); //Fill list until there is no more memory in the buffer try{ while(1) { mylist->insert(mylist->begin(), 0); } } catch(const bad_alloc &){ //memory is full } //Let's obtain the size of the list MyList::size_type old_size = mylist->size(); //To make the list bigger, let's increase the heap buffer //in 1000 bytes more. heap_memory.grow(1000); //If memory has been reallocated, the old pointer is invalid, so //use previously obtained handle to find the new pointer. mylist = static_cast<MyList *> (heap_memory.get_address_from_handle(list_handle)); //Fill list until there is no more memory in the buffer try{ while(1) { mylist->insert(mylist->begin(), 0); } } catch(const bad_alloc &){ //memory is full } //Let's obtain the new size of the list MyList::size_type new_size = mylist->size(); assert(new_size > old_size); //Destroy list heap_memory.destroy_ptr(mylist); return 0; }
Все сегменты управляемой памяти имеют сходные возможности (распределение памяти внутри сегмента памяти, названное конструкцией объекта...), но есть некоторые замечательные различия междууправляемой_общей_памятью,управляемой_mapped_файломиуправляемой_heap_памятью,управляемой_внешней_файлом.
Чтобы увидеть полезность управляемой кучной памяти и управляемых внешних буферных классов, следующий пример показывает, как можно использовать очередь сообщений для сериализации целой базы данных, построенной в буфере памяти с использованиемBoost.Interprocess, отправить базу данных через очередь сообщений и дублировать в другом буфере:
//This test creates a in memory data-base using Interprocess machinery and //serializes it through a message queue. Then rebuilds the data-base in //another buffer and checks it against the original data-base bool test_serialize_db() { //Typedef data to create a Interprocess map typedef std::pair<const std::size_t, std::size_t> MyPair; typedef std::less<std::size_t> MyLess; typedef node_allocator<MyPair, managed_external_buffer::segment_manager> node_allocator_t; typedef map<std::size_t, std::size_t, std::less<std::size_t>, node_allocator_t> MyMap; //Some constants const std::size_t BufferSize = 65536; const std::size_t MaxMsgSize = 100; //Allocate a memory buffer to hold the destiny database using vector<char> std::vector<char> buffer_destiny(BufferSize, 0); message_queue::remove(test::get_process_id_name()); { //Create the message-queues message_queue mq1(create_only, test::get_process_id_name(), 1, MaxMsgSize); //Open previously created message-queue simulating other process message_queue mq2(open_only, test::get_process_id_name()); //A managed heap memory to create the origin database managed_heap_memory db_origin(buffer_destiny.size()); //Construct the map in the first buffer MyMap *map1 = db_origin.construct<MyMap>("MyMap") (MyLess(), db_origin.get_segment_manager()); if(!map1) return false; //Fill map1 until is full try{ std::size_t i = 0; while(1){ (*map1)[i] = i; ++i; } } catch(boost::interprocess::bad_alloc &){} //Data control data sending through the message queue std::size_t sent = 0; message_queue::size_type recvd = 0; message_queue::size_type total_recvd = 0; unsigned int priority; //Send whole first buffer through the mq1, read it //through mq2 to the second buffer while(1){ //Send a fragment of buffer1 through mq1 std::size_t bytes_to_send = MaxMsgSize < (db_origin.get_size() - sent) ? MaxMsgSize : (db_origin.get_size() - sent); mq1.send( &static_cast<char*>(db_origin.get_address())[sent] , bytes_to_send , 0); sent += bytes_to_send; //Receive the fragment through mq2 to buffer_destiny mq2.receive( &buffer_destiny[total_recvd] , BufferSize - recvd , recvd , priority); total_recvd += recvd; //Check if we have received all the buffer if(total_recvd == BufferSize){ break; } } //The buffer will contain a copy of the original database //so let's interpret the buffer with managed_external_buffer managed_external_buffer db_destiny(open_only, &buffer_destiny[0], BufferSize); //Let's find the map std::pair<MyMap *, managed_external_buffer::size_type> ret = db_destiny.find<MyMap>("MyMap"); MyMap *map2 = ret.first; //Check if we have found it if(!map2){ return false; } //Check if it is a single variable (not an array) if(ret.second != 1){ return false; } //Now let's compare size if(map1->size() != map2->size()){ return false; } //Now let's compare all db values MyMap::size_type num_elements = map1->size(); for(std::size_t i = 0; i < num_elements; ++i){ if((*map1)[i] != (*map2)[i]){ return false; } } //Destroy maps from db-s db_origin.destroy_ptr(map1); db_destiny.destroy_ptr(map2); } message_queue::remove(test::get_process_id_name()); return true; }
Статья Managed Memory Segments раздела The Boost C++ Libraries BoostBook Documentation Subset Chapter 16. Boost.Interprocess может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.
:: Главная :: Chapter 16. Boost.Interprocess ::
реклама |