使进程间数据通信变容易 托管共享内存 托管映射文件 托管内存片段的特性 托管内存片段的高级特性 托管堆内存和托管外部缓冲区 |
介绍 声明托管内存片段类 |
正如我们所看到的,Boost.Interprocess提供一些基本的类来构建共享内存对象和文件映射,然后映射这些可映射的类至进程的地址空间。
然而,管理这些内存片段对一些特殊任务来说没那么容易。一个映射区域是一个固定长度的内存缓冲区,能够动态的创建和销毁任意类型的对象,这需要非常多的工作量,因为这需要为那部分内存编写一个内存管理算法。很多时候,我们还想在共享内存上关联对象和其名称,以便所有进程能通过名称找到对象。
Boost.Interprocess提供4个托管内存片段类:
前两个类管理能在进程间共享的内存片段。第三个类用于创建复杂的基数据,它通过其他机制例如消息队列发送至其他进程。第四个类能管理任意固定大小的内存缓冲区。前两个类将在下一小节介绍。
basic_managed_heap_memory 和 basic_managed_external_buffer将稍后做解释。
托管内存段的最重要的服务是:
所有的Boost.Interprocess托管内存片段类都是模板类,可以由用户定制。
template < class CharType, class MemoryAlgorithm, template<class IndexConfig> class IndexType > class basic_managed_shared_memory / basic_managed_mapped_file / basic_managed_heap_memory / basic_external_buffer;
这些类可以使用以下的模板参数定制:
这样,我们能使用char或wchar_t字符串来分辨在内存片段中创建的C++对象,我们能够插入新的共享内存分配算法,并且使用最合适的索引类型。
常见的托管共享内存类 构建托管共享内存 使用原生Windows共享内存 使用XSI(系统V)共享内存 |
如所示,basic_managed_shared_memory提供了多种定制方式。但对一般用户而言,需要一个常用的、缺省的共享内存具名对象创建。基于此原因,Boost.Interprocess定义了最常用的托管共享内存特例:
//!Defines a managed shared memory with c-strings as keys for named objects, //!the default memory algorithm (with process-shared mutexes, //!and offset_ptr as internal pointers) as memory allocation algorithm //!and the default index type as the index. //!This class allows the shared memory to be mapped in different base //!in different processes typedef basic_managed_shared_memory<char ,/*Default memory algorithm defining offset_ptr<void> as void_pointer*/ ,/*Default index type*/> managed_shared_memory; //!Defines a managed shared memory with wide strings as keys for named objects, //!the default memory algorithm (with process-shared mutexes, //!and offset_ptr as internal pointers) as memory allocation algorithm //!and the default index type as the index. //!This class allows the shared memory to be mapped in different base //!in different processes typedef basic_managed_shared_memory<wchar_t ,/*Default memory algorithm defining offset_ptr<void> as void_pointer*/ ,/*Default index type*/> wmanaged_shared_memory;
managed_shared_memory在共享内存上分配关联c字符串的对象,wmanaged_shared_memory在共享内存上分配关联以wchar_t空字符结尾的字符串的对象。它们都定义了指针类型offset_ptr<void>,因此它们能被用来映射共享内存至不同进程的不同基地址上。
如果用户想映射共享内存至所有进程的相同地址上,并且想使用原始内部指针来代替偏移指针,Boost.Interprocess定义了如下类型:
//!Defines a managed shared memory with c-strings as keys for named objects, //!the default memory algorithm (with process-shared mutexes, //!and offset_ptr as internal pointers) as memory allocation algorithm //!and the default index type as the index. //!This class allows the shared memory to be mapped in different base //!in different processes*/ typedef basic_managed_shared_memory <char ,/*Default memory algorithm defining void * as void_pointer*/ ,/*Default index type*/> fixed_managed_shared_memory; //!Defines a managed shared memory with wide strings as keys for named objects, //!the default memory algorithm (with process-shared mutexes, //!and offset_ptr as internal pointers) as memory allocation algorithm //!and the default index type as the index. //!This class allows the shared memory to be mapped in different base //!in different processes typedef basic_managed_shared_memory <wchar_t ,/*Default memory algorithm defining void * as void_pointer*/ ,/*Default index type*/> wfixed_managed_shared_memory;
托管共享内存是一个高级类,它结合了共享内存对象和覆盖所有共享内存对象的映射区域。那意味着如果我们创建了一个新的托管共享内存:
当我们打开一个托管内存段:
要使用一个托管共享内存,你必须包含以下头文件:
#include <boost/interprocess/managed_shared_memory.hpp> //1. Creates a new shared memory object // called "MySharedMemory". //2. Maps the whole object to this // process' address space. //3. Constructs some objects in shared memory // to implement managed features. //!! If anything fails, throws interprocess_exception // managed_shared_memory segment ( create_only , "MySharedMemory" //Shared memory object name , 65536); //Shared memory object size in bytes //1. Opens a shared memory object // called "MySharedMemory". //2. Maps the whole object to this // process' address space. //3. Obtains pointers to constructed internal objects // to implement managed features. //!! If anything fails, throws interprocess_exception // managed_shared_memory segment (open_only, "MySharedMemory");//Shared memory object name //1. If the segment was previously created // equivalent to "open_only" (size is ignored). //2. Otherwise, equivalent to "create_only" //!! If anything fails, throws interprocess_exception // managed_shared_memory segment ( open_or_create , "MySharedMemory" //Shared memory object name , 65536); //Shared memory object size in bytes
当对象managed_shared_memory被销毁,共享内存对象自动被取消映射,并且所有资源被释放。为了在系统中删除共享内存对象,你必须使用shared_memory_object::remove函数。共享内存对象的删除可能会失败,如果有其他进程仍旧拥有映射的共享内存对象。
用户也可以映射托管共享内存至固定的地址上。当使用fixed_managed_shared_memory时,这是必需的。为达此目的,仅需在一个额外的参数上添加映射地址:
fixed_managed_shared_memory segment (open_only ,"MyFixedAddressSharedMemory" //Shared memory object name ,(void*)0x30000000 //Mapping address
Windows用户可能想使用原生windows共享内存来替代可移植的shared_memory_object托管内存。可以通过类basic_managed_windows_shared_memory 达到此目的。要使用它,仅需包含:
#include <boost/interprocess/managed_windows_shared_memory.hpp>
此类具有与 basic_managed_shared_memory 相同的接口但使用的是原生windows共享内存。注意到此托管类具有与windows共享内存相同的生命周期问题:当最后一个关联windows共享内存的进程与此内存分离了(或结束/崩溃),此内存将被销毁。因此windows共享内存没有持久性支持。
要使用managed_windows_shared_memory在系统服务和用户应用间通信,请阅读章节 Native windowsshared memory中的解释。
Unix用户可能也想使用XSI(系统V)来代替可移植的shared_memory_object托管内存。可以通过类basic_managed_xsi_shared_memory 达到此目的。要使用它,仅需包含:
#include <boost/interprocess/managed_xsi_shared_memory.hpp>
此类具有和 basic_managed_shared_memory 几乎相同的接口,但使用的是XSI共享内存做为后端。
更多关于操作XSI共享内存能力的信息,请参考 basic_managed_xsi_shared_memory
常见的托管映射文件 构建托管映射文件 |
如所示,basic_managed_mapped_file提供了多种定制方式。但对一般用户而言,需要一个常用的、缺省的共享内存具名对象创建。基于此原因,Boost.Interprocess定义了最常用的托管映射文件特例:
//Named object creation managed memory segment //All objects are constructed in the memory-mapped file // Names are c-strings, // Default memory management algorithm(rbtree_best_fit with no mutexes) // Name-object mappings are stored in the default index type (flat_map) typedef basic_managed_mapped_file < char, rbtree_best_fit<mutex_family, offset_ptr<void> >, flat_map_index > managed_mapped_file; //Named object creation managed memory segment //All objects are constructed in the memory-mapped file // Names are wide-strings, // Default memory management algorithm(rbtree_best_fit with no mutexes) // Name-object mappings are stored in the default index type (flat_map) typedef basic_managed_mapped_file< wchar_t, rbtree_best_fit<mutex_family, offset_ptr<void> >, flat_map_index > wmanaged_mapped_file;
managed_mapped_file在内存映射文件上分配关联c字符串的对象,wmanaged_mapped_file在内存映射文件上分配关联以wchar_t空字符结尾的字符串的对象。它们都定义了指针类型offset_ptr<void>,因此它们能被用来映射映射文件至不同进程的不同基地址上。
托管映射文件是一个高级类,它结合了文件和覆盖所有文件的映射区域。那意味着如果我们创建了一个新的托管映射文件:
当我们打开一个托管映射文件
要使用一个托管映射文件,你必须包含以下头文件:
#include <boost/interprocess/managed_mapped_file.hpp> //1. Creates a new file // called "MyMappedFile". //2. Maps the whole file to this // process' address space. //3. Constructs some objects in the memory mapped // file to implement managed features. //!! If anything fails, throws interprocess_exception // managed_mapped_file mfile (create_only, "MyMappedFile", //Mapped file name 65536); //Mapped file size //1. Opens a file // called "MyMappedFile". //2. Maps the whole file to this // process' address space. //3. Obtains pointers to constructed internal objects // to implement managed features. //!! If anything fails, throws interprocess_exception // managed_mapped_file mfile (open_only, "MyMappedFile"); //Mapped file name[c++] //1. If the file was previously created // equivalent to "open_only". //2. Otherwise, equivalent to "open_only" (size is ignored) // //!! If anything fails, throws interprocess_exception // managed_mapped_file mfile (open_or_create, "MyMappedFile", //Mapped file name 65536); //Mapped file size
当对象managed_mapped_file被销毁,文件自动被取消映射,并且所有资源被释放。为了在文件系统中删除文件,你必须使用标准的C函数 std::remove或Boost.Filesystem的remove() 函数。但是文件的删除可能会失败,如果有其他进程仍旧拥有映射至内存上的文件或文件正被其他进程打开。
要获得移植性更高的行为,可以使用file_mapping::remove(const char *)操作,它将删除文件即使文件正被映射着。然而,在一些操作系统下,如果没有文件删除权限(例如C++文件流),删除将失败。但在多数情况下,file_mapping::remove具有足够高的移植性。
更多关于操作映射文件能力的信息,请参考 basic_managed_mapped_file。
支配托管内存片段 获取句柄以辨别数据 对象构造函数族 匿名实例构建 单例构建 同步保障 名称/对象映射表索引类型 片段管理 获取构建对象的信息 原子执行对象函数 |
以下特征对所有的托管内存段类都是常见的,但我们将在例子中使用托管共享内存。使用内存映射文件或其他托管内存段类,我们能达到相同的效果。
如果需要从托管内存段(例如,托管共享内存)分配基本的原始字节,用于执行最高级进程间通信,此类提供了分配和释放函数。分配函数有抛异常和不抛异常的版本。如果内存不够,抛异常版本将抛 boost::interprocess::bad_alloc(派生自std::bad_alloc异常),不抛异常版本返回空指针。
#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("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", 65536); //Allocate 100 bytes of memory from segment, throwing version void *ptr = managed_shm.allocate(100); //Deallocate it managed_shm.deallocate(ptr); //Non throwing version ptr = managed_shm.allocate(100, std::nothrow); //Deallocate it managed_shm.deallocate(ptr); return 0; }
此类也能提供在托管内存段绝对地址与能被任何进程间通信机制使用的句柄间的转换。当使用包含此对象的托管内存段,此句柄能被再次转化为一个绝对地址。句柄能在进程间当关键字使用,用于辨别托管内存段的已分配部分或构建在托管片段上的对象。
//Process A obtains the offset of the address managed_shared_memory::handle handle = segment.get_handle_from_address(processA_address); //Process A sends this address using any mechanism to process B //Process B obtains the handle and transforms it again to an address managed_shared_memory::handle handle = ... void * processB_address = segment.get_address_from_handle(handle);
当在托管内存段(托管共享内存、托管映射文件等)上构件关联一个名字的对象时,用户有多种对象构建族用于“构件”或“如果未找到,则构建”。Boost.Interprocess能构建单个对象或一组对象。可以采用相同的参数构建对象组或通过迭代器列表定义各自的参数:
//!Allocates and constructs an object of type MyType (throwing version) MyType *ptr = managed_memory_segment.construct<MyType>("Name") (par1, par2...); //!Allocates and constructs an array of objects of type MyType (throwing version) //!Each object receives the same parameters (par1, par2, ...) MyType *ptr = managed_memory_segment.construct<MyType>("Name")[count](par1, par2...); //!Tries to find a previously created object. If not present, allocates //!and constructs an object of type MyType (throwing version) MyType *ptr = managed_memory_segment.find_or_construct<MyType>("Name") (par1, par2...); //!Tries to find a previously created object. If not present, allocates and //!constructs an array of objects of type MyType (throwing version). Each object //!receives the same parameters (par1, par2, ...) MyType *ptr = managed_memory_segment.find_or_construct<MyType>("Name")[count](par1, par2...); //!Allocates and constructs an array of objects of type MyType (throwing version) //!Each object receives parameters returned with the expression (*it1++, *it2++,... ) MyType *ptr = managed_memory_segment.construct_it<MyType>("Name")[count](it1, it2...); //!Tries to find a previously created object. If not present, allocates and constructs //!an array of objects of type MyType (throwing version). Each object receives //!parameters returned with the expression (*it1++, *it2++,... ) MyType *ptr = managed_memory_segment.find_or_construct_it<MyType>("Name")[count](it1, it2...); //!Tries to find a previously created object. Returns a pointer to the object and the //!count (if it is not an array, returns 1). If not present, the returned pointer is 0 std::pair<MyType *,std::size_t> ret = managed_memory_segment.find<MyType>("Name"); //!Destroys the created object, returns false if not present bool destroyed = managed_memory_segment.destroy<MyType>("Name"); //!Destroys the created object via pointer managed_memory_segment.destroy_ptr(ptr);
所有这些函数都有不抛出异常的版本,通过额外参数std::nothrow指定。例如,对简单对象构建:
//!Allocates and constructs an object of type MyType (no throwing version) MyType *ptr = managed_memory_segment.construct<MyType>("Name", std::nothrow) (par1, par2...);
有时,用户不想创建关联名字的对象。基于此目的,Boost.Interprocess能在托管内存段上构建匿名对象。所有具名对象构建函数都可用于构建匿名对象。为构建一个匿名对象,用户必须使用名称"boost::interprocess::anonymous_instance" 代替一般名称:
MyType *ptr = managed_memory_segment.construct<MyType>(anonymous_instance) (par1, par2...); //Other construct variants can also be used (including non-throwing ones) ... //We can only destroy the anonymous object via pointer managed_memory_segment.destroy_ptr(ptr);
查找函数在这里没有任何意义,因为匿名对象没有名称。我们只能通过指针来销毁匿名对象。
有时,用户想在托管内存段上模拟一个单例。显然,由于托管内存段是在运行时构建的,因此用户必须显式的构建和销毁此对象。但是用户如何保证此对象是此托管内存段上的此类型唯一对象呢?可以使用一个具名对象来模拟,并检查在尝试创建一个前,它是否存在,但是所有进程必须认同对象的名称,这可能导致与其他已存在名字的冲突。
为解决此问题,Boost.Interprocess提供了在托管内存段中创建一个“唯一对象”。使用此“唯一对象”服务,仅有类的一个实例能够创建在托管内存段中(尽管你可以创建此类的多个具名对象),因此模拟进程间类单例对象变得更容易了,例如设计一个汇集的、共享内存分配器。此对象能够使用此类的类型做为关键字进行查找。
// Construct MyType *ptr = managed_memory_segment.construct<MyType>(unique_instance) (par1, par2...); // Find it std::pair<MyType *,std::size_t> ret = managed_memory_segment.find<MyType>(unique_instance); // Destroy it managed_memory_segment.destroy<MyType>(unique_instance); // Other construct and find variants can also be used (including non-throwing ones) //... // We can also destroy the unique object via pointer MyType *ptr = managed_memory_segment.construct<MyType>(unique_instance) (par1, par2...); managed_shared_memory.destroy_ptr(ptr);
查找函数获得具有类型T的唯一对象指针,它使用“唯一对象”机制创建。
具名/唯一的分配/查找/析构的一个特征是它们都是原子的。具名分配器使用递归同步方案,此方案由内部互斥量族定义的内存分配算法模板参数(MemoryAlgorithm)指定。也即,用于同步具名/唯一分配器的互斥量类型是由类型MemoryAlgorithm::mutex_family::recursive_mutex_type定义的。对基于托管片段的共享内存和内存映射文件,此递归互斥量被定义为interprocess_recursive_mutex.
如果两个进程能同时调用:
MyType *ptr = managed_shared_memory.find_or_construct<MyType>("Name")[count](par1, par2...);
但只有一个进程能创建此对象,另外一个将获得已创建对象的指针。
当执行具名/匿名/唯一分配时,使用allocate()的原始分配器也能安全被调用,就好像编制一个多线程应用,此应用插入一个对象在互斥量保护的映射表中,当映射线程正在搜索插入新对象的位置时通过调用new[]而不阻塞其他线程。一旦映射表找到了正确的位置,并且它必须分配原始内存来构建新值时,同步过程确实发生了。
这意味着如果我们正在创建或查找大量的具名对象时,我们仅阻塞来自其他进程的创建/查找,但如果只是插入元素至共享内存数组中时,我们不阻塞其他进程。
如上所示,当创建具名对象时,托管内存段存储名称/对象关联至索引中。此索引是一个映射表,它使用对象的名字做键,指向对象的指针做值。默认实例managed_shared_memory 和 wmanaged_shared_memory使用flat_map_index做为索引类型。
每个索引都有其自身的特性,例如查找-时间、插入时间、删除时间、内存使用和内存分配模式。Boost.Interprocess目前提供3种索引类型:
做为一个例子,如果我们想定义使用boost::interprocess::map 为索引类型的新托管共享内存类,我们仅需指定 [boost::interprocess::map_indexmap_index] 做为模板参数。
//This managed memory segment can allocate objects with: // -> a wchar_t string as key // -> boost::interprocess::rbtree_best_fit with process-shared mutexes // as memory allocation algorithm. // -> boost::interprocess::map<...> as the index to store name/object mappings // typedef boost::interprocess::basic_managed_shared_memory < wchar_t , boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family, offset_ptr<void> > , boost::interprocess::map_index > my_managed_shared_memory;
一旦此容器被引入至Boost,Boost.Interprocess计划提供一个基于unordered_map的索引。如果这些索引对你来说还不够,你可以定义你自己的索引类型。欲了解如何做到这点,参考Building customindexes 章节。
所有的Boost.Interprocess托管内存段类都在它们自己的内存片段(共享内存、内存映射文件、堆内存等)中构建一些结构来执行内存管理算法、具名分配器、同步对象等。所有这些对象都封装在一个叫做片段管理(segment manager)的单例对象中。一个托管内存映射文件和托管共享内存使用相同的片段管理来执行所有的托管内存段特性,这主要基于片段管理是一个管理固定大小内存缓冲区的类的事实。由于共享内存或内存映射文件都是通过映射区域访问,并且映射区域是一个固定大小的缓冲区,因此单例片段管理类能够管理多种托管内存段类型。
一些Boost.Interprocess类在其构造函数中需要一个指向片段管理的指针,并且片段管理能从任意托管内存段通过成员函数get_segment_manager获得:
managed_shared_memory::segment_manager *seg_manager = managed_shm.get_segment_manager();
一旦使用construct<>函数族构建了一个对象,程序员可以通过指向此对象的指针来获取对象信息。程序员能获得如下信息:
以下是功能示例:
#include <boost/interprocess/managed_shared_memory.hpp> #include <cassert> #include <cstring> class my_class { //... }; 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_shared_memory managed_shm(create_only, "MySharedMemory", 10000*sizeof(std::size_t)); //Construct objects my_class *named_object = managed_shm.construct<my_class>("Object name")[1](); my_class *unique_object = managed_shm.construct<my_class>(unique_instance)[2](); my_class *anon_object = managed_shm.construct<my_class>(anonymous_instance)[3](); //Now test "get_instance_name" function. assert(0 == std::strcmp(managed_shared_memory::get_instance_name(named_object), "Object name")); assert(0 == managed_shared_memory::get_instance_name(unique_object)); assert(0 == managed_shared_memory::get_instance_name(anon_object)); //Now test "get_instance_type" function. assert(named_type == managed_shared_memory::get_instance_type(named_object)); assert(unique_type == managed_shared_memory::get_instance_type(unique_object)); assert(anonymous_type == managed_shared_memory::get_instance_type(anon_object)); //Now test "get_instance_length" function. assert(1 == managed_shared_memory::get_instance_length(named_object)); assert(2 == managed_shared_memory::get_instance_length(unique_object)); assert(3 == managed_shared_memory::get_instance_length(anon_object)); managed_shm.destroy_ptr(named_object); managed_shm.destroy_ptr(unique_object); managed_shm.destroy_ptr(anon_object); return 0; }
有时程序员必须执行一些代码,且需要在执行代码时保证没有其他进程或线程会创建或销毁任何具名、唯一或匿名对象。用户可能想创建一些具名对象并初始化它们,但是这些对象能马上被其他进程可用。
为达此目的,程序员可以使用托管类提供的atomic_func()函数:
//This object function will create several named objects create_several_objects_func func(/**/); //While executing the function, no other process will be //able to create or destroy objects managed_memory.atomic_func(func);
注意到 atomic_func并不阻止其他进程分配原始内存或为已构建的对象执行成员函数(例如:另一个进程可能推元素至置于片段中的向量里)。此原子函数仅阻塞来自于其他进程的具名、唯一和匿名创建、查找和析构(并发调用construct<>, find<>, find_or_construct<>, destroy<>等等)。
获得关于托管片段的信息 增长的托管片段 高级索引函数 分配对齐的内存部分 多种分配函数 扩展内存分配 用写拷贝或只读模式打开托管共享内存或内存映射文件 |
以下这些函数能获得关于托管内存段的信息:
获得内存片段大小:
managed_shm.get_size();
获得内存片段的剩余字节:
managed_shm.get_free_memory();
将内存清空至0:
managed_shm.zero_free_memory();
了解是否所有内存被释放了,如果没有,返回false:
managed_shm.all_memory_deallocated();
测试托管片段的内部结构。如果未检测到错误,返回true:
managed_shm.check_sanity();
获得分配在片段中的具名和唯一对象的数目:
managed_shm.get_num_named_objects(); managed_shm.get_num_unique_objects();
一旦托管片段被创建,它就不能增长了。此限制不太容易解决:每个关联此托管片段的进程必须停止,被通知新的大小,然后它们需要重新映射托管片段再继续运行。没有系统内核的帮助,仅依靠用户级库几乎无法完成这些操作。
另一方面,Boost.Interprocess提供了线下片段增长。这意味着什么?也就是如果没有其他进程映射托管片段,此片段能被增长。如果应用程序能够找到没有进程关联片段的时刻,它能增长或缩减以适应托管片段。
这里我们有个例子演示如何增长和缩减以适应managed_shared_memory:
#include <boost/interprocess/managed_shared_memory.hpp> #include <boost/interprocess/managed_mapped_file.hpp> #include <cassert> class MyClass { //... }; 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; { //Create a managed shared memory managed_shared_memory shm(create_only, "MySharedMemory", 1000); //Check size assert(shm.get_size() == 1000); //Construct a named object MyClass *myclass = shm.construct<MyClass>("MyClass")(); //The managed segment is unmapped here } { //Now that the segment is not mapped grow it adding extra 500 bytes managed_shared_memory::grow("MySharedMemory", 500); //Map it again managed_shared_memory shm(open_only, "MySharedMemory"); //Check size assert(shm.get_size() == 1500); //Check "MyClass" is still there MyClass *myclass = shm.find<MyClass>("MyClass").first; assert(myclass != 0); //The managed segment is unmapped here } { //Now minimize the size of the segment managed_shared_memory::shrink_to_fit("MySharedMemory"); //Map it again managed_shared_memory shm(open_only, "MySharedMemory"); //Check size assert(shm.get_size() < 1000); //Check "MyClass" is still there MyClass *myclass = shm.find<MyClass>("MyClass").first; assert(myclass != 0); //The managed segment is unmapped here } return 0; }
managed_mapped_file 也提供了类似的函数用于增长或缩减以适应托管文件。请记住在增长/缩减被执行时,任何进程都不能修改文件/共享内存。否则,托管片段将被损坏。
正如提到的,托管片段存储具名和唯一对象的信息至两个索引中。依赖于这些索引的类型,当新的具名或唯一分配器被创建时,此索引必须重分配一些辅助结构。对一些索引来说,如果用户知道将创建多少具名或唯一对象时,可以提前分配一些结构来获得更好的性能。(如果索引是一个有序向量,则可以提前分配内存避免重分配。如果索引是一个哈希结构,则可以提前分配桶数组)。
以下函数保留了内存,以使对具名或唯一对象的后续分配更有效。这些函数仅对伪浸入或非节点索引(例如flat_map_index,iunorderd_set_index)有效。这些函数对默认索引(iset_index)或其他索引(map_index)无效:
managed_shm.reserve_named_objects(1000); managed_shm.reserve_unique_objects(1000); managed_shm.reserve_named_objects(1000); managed_shm.reserve_unique_objects(1000);
托管内存片段也提供了通过已创建的具名和唯一对象进行迭代调试的可能性。小心:此类迭代不是线程安全的,因此用户必须保证没有其他线程正在片段中使用(创建、删除、预留)
具名或唯一索引。其他不牵涉到索引的操作可以并发执行(例如,原始内存分配/释放)。
以下函数返回表示具名和唯一对象存储在托管片段范围的常量迭代器。依赖于索引类型,当一个具名或唯一创建/删除/预留操作进行后,迭代器可能会失效:
typedef managed_shared_memory::const_named_iterator const_named_it; const_named_it named_beg = managed_shm.named_begin(); const_named_it named_end = managed_shm.named_end(); typedef managed_shared_memory::const_unique_iterator const_unique_it; const_unique_it unique_beg = managed_shm.unique_begin(); const_unique_it unique_end = managed_shm.unique_end(); for(; named_beg != named_end; ++named_beg){ //A pointer to the name of the named object const managed_shared_memory::char_type *name = named_beg->name(); //The length of the name std::size_t name_len = named_beg->name_length(); //A constant void pointer to the named object const void *value = named_beg->value(); } for(; unique_beg != unique_end; ++unique_beg){ //The typeid(T).name() of the unique object const char *typeid_name = unique_beg->name(); //The length of the name std::size_t name_len = unique_beg->name_length(); //A constant void pointer to the unique object const void *value = unique_beg->value(); }
有时,由于软硬件的限制,能够分配对齐的内存片段是很有趣的。有时候,拥有对齐的内存是一项可以用来提升某些内存算法的特性。
这种分配与之前展示的原始内存分配类似,但它需要一个额外的参数来指定对齐。关于对齐有一个限制:它必须是2的幂次方。
如果一个用户想分配许多对齐的内存块(例如对齐到128字节),则内存浪费的最小值是一个该值的倍数(例如2*128 - 一些字节)。导致此的原因是每种内存分配器通常需要一些额外的元数据在此被分配的缓冲区的头一批字节上。如果用户知道”一些字节”的值并且如果空闲内存块的头几个字节被用来填充对齐的分配,内存块的剩余部分也可以对齐并且为下次对齐分配做好准备。注意到请求一个对齐值整数倍的值并不是最优的,因为由于所需的元数据导致余下的内存块不是对齐的。
一旦编程者了解每次内存分配的有效载荷数,他能申请一个最优值,此值能达到申请值和未来对齐分配值的最大化组合。
下面是一个小例子,展示了如何使用对齐分配:
#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", 65536); const std::size_t Alignment = 128; //Allocate 100 bytes aligned to Alignment from segment, throwing version void *ptr = managed_shm.allocate_aligned(100, Alignment); //Check alignment assert((static_cast<char*>(ptr)-static_cast<char*>(0)) % Alignment == 0); //Deallocate it managed_shm.deallocate(ptr); //Non throwing version ptr = managed_shm.allocate_aligned(100, Alignment, std::nothrow); //Check alignment assert((static_cast<char*>(ptr)-static_cast<char*>(0)) % Alignment == 0); //Deallocate it managed_shm.deallocate(ptr); //If we want to efficiently allocate aligned blocks of memory //use managed_shared_memory::PayloadPerAllocation value assert(Alignment > managed_shared_memory::PayloadPerAllocation); //This allocation will maximize the size of the aligned memory //and will increase the possibility of finding more aligned memory ptr = managed_shm.allocate_aligned (3*Alignment - managed_shared_memory::PayloadPerAllocation, Alignment); //Check alignment assert((static_cast<char*>(ptr)-static_cast<char*>(0)) % Alignment == 0); //Deallocate it managed_shm.deallocate(ptr); return 0; }
如果一个应用程序需要分配许多内存缓冲区,但同时必须独立的释放它们,应用程序一般被迫循环调用allocate()。托管内存片段提供了一个迭代函数用于在单个获取内存缓冲区的调用中打包数个分配器:
这种分配方法比循环调用allocate()要快许多。缺点是内存片段必须提供一块足够大的连续内存段以放置所有的分配器。
托管内存片段通过allocate_many()函数族提供了这些功能。下面是2种allocate_many函数:
//!Allocates n_elements of elem_size bytes. multiallocation_iterator allocate_many(std::size_t elem_size, std::size_t min_elements, std::size_t preferred_elements, std::size_t &received_elements); //!Allocates n_elements, each one of elem_sizes[i] bytes. multiallocation_iterator allocate_many(const std::size_t *elem_sizes, std::size_t n_elements); //!Allocates n_elements of elem_size bytes. No throwing version. multiallocation_iterator allocate_many(std::size_t elem_size, std::size_t min_elements, std::size_t preferred_elements, std::size_t &received_elements, std::nothrow_t nothrow); //!Allocates n_elements, each one of elem_sizes[i] bytes. No throwing version. multiallocation_iterator allocate_many(const std::size_t *elem_sizes, std::size_t n_elements, std::nothrow_t nothrow);
所有函数返回一个多分配迭代器,它能被用于获得用户可覆盖的内存的指针。一个多分配迭代器:
以下是一个小例子,展示了所有这些功能:
#include <boost/interprocess/managed_shared_memory.hpp> #include <boost/interprocess/detail/move.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(100, 16, std::nothrow)); //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.front(); 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; chain = managed_shm.allocate_many(sizes, 10); managed_shm.deallocate_many(boost::move(chain)); return 0; }
分配N个相同大小的缓冲区能提升池子和节点容器(例如类似于STL的链表)的性能:当插入一系列向前指针至类STL链表中,插入函数能够检测需要的元素数目并且分配一个单独调用。节点也能被释放。
分配N个不同大小的缓冲区能用于加速以下情况的分配:当数个对象必须总是在同一时间分配但在不同时间释放。例如,一个类可能执行数个初始化分配(例如一些网络包得头数据)在其构造函数中,同时也分配一块将来可能会被释放的缓冲区(通过网络传送的数据)。构造函数可能使用allocate_many()来加速初始化,而不是独立分配所有数据,但它也能释放并且增加可变大小元素的内存。
一般而言,allocate_many()当N很大时非常有用。过度使用allocate_many()会降低(译注:原E文此处为提高(increase))有效内存使用率,因为它不能重用已存在的不连续内存碎片,而这些碎片原本可以被一些元素使用。
当编程一些数据结构例如向量,内存重分配变为提高性能的重要工具。托管内存片段提供了一个高级的重分配函数,它能提供:
扩展能与新缓冲区的分配结合使用,如果扩展没有获得一个带“扩展,如果分配失败一个新缓冲区”语义的函数。
除了这个功能,此函数总返回分配缓冲区的实际大小,因为很多时候,基于对齐问题,分配的缓冲区会比实际申请的大一些。所以,程序员可以使用分配命令来增大内存。
以下是函数声明:
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 , std::size_t preferred_size , std::size_t &received_size , T *reuse_ptr = 0);
此函数的前提条件:
此函数的效果:
如果两个条件满足了,将抛异常:
此函数返回:
注意:
如果用户选择了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, preferred_size = 1000; managed_shared_memory::size_type received_size; std::size_t *ptr = managed_shm.allocation_command<std::size_t> (boost::interprocess::allocate_new, min_size, preferred_size, received_size).first; //Received size must be bigger than min_size assert(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 < 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 managed_shared_memory::size_type expanded_size; std::pair<std::size_t *, bool> ret = managed_shm.allocation_command (boost::interprocess::expand_fwd, received_size*2, received_size*3, expanded_size, ptr); //Check invariants assert(ret.second == true); assert(ret.first == ptr); assert(expanded_size >= 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 = 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; ret = managed_shm.allocation_command (boost::interprocess::shrink_in_place, min_size*2, min_size, shrunk_size, ptr); //Check invariants assert(ret.second == true); assert(ret.first == 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是非常强大的函数,它能引起重要的性能提升。在编写类似于vector的数据结构时尤其有用,在此情况下,程序员能够使分配内存请求数目和内存浪费都达到最小。
当映射一个基于共享内存或文件的内存片段时,可以选择open_copy_on_write选项来打开它们。此选项类似与open_only但是每一个程序员对此托管片段的改变都仅对此进程私有,并且不会传导至底层设备(共享内存或文件)。
底层的共享内存或文件被read_only打开,因此多个进程可以共享一个初始内存片段并且使修改对其私有。如果很多进程以写拷贝并且不修改的方式打开一个托管片段,则托管片段的页面将在所有进程间共享,这节省了很多内存。
用open_read_only模式打开托管共享内存或映射文件,将以read-only属性映射底层设备至内存中。这意味着任何尝试写此内存的操作,不管是创建对象或是锁互斥量都会导致操作系统页面缺陷错误(然后,程序中止)。Read-only模式以只读模式打开底层设备(共享内存、文件等)并且如果多个进程仅想处理一个托管内存片段而不修改它时,这将节省可观的内存消耗。Read-only模式操作限制如下:
以下是例子,它展示了这两种打开模式的使用:
#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对象在用户提供的缓冲区中 托管堆内存:Boost.Interprocess对内存机制 与托管内存片段的不同之处 例子:通过消息队列序列化一个数据库 |
Boost.Interprocess使用managed_shared_memory或managed_mapped_file为进程间提供了托管共享内存。两个进程仅需映射相同的可映射内存资源,然后在此对象上读写。
很多时候,我们不想使用这种共享内存方式,我们更希望通过网络、本地socket或消息队列发送序列化的数据。序列化过程可以使用 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>
让我们看看一个使用managed_external_buffer的例子:
#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.Interprocess的STL兼容分配器也能被用于放置STL兼容容器在用户内存片段中。
basic_managed_external_buffer在嵌入式系统中构建小型数据库时也是非常有用的,它能限制使用的内存大小至预定义的内存块,防止数据库分割堆内存。
使用堆内存(new/delete)来获得用于用户存储所有其数据的缓冲区是非常普遍的,因此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完全相同,除了托管内存片段使用动态的(new/delete)方法创建内存之外。
basic_managed_heap_memory也提供了一个增长(std::size_t extra_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; }
所有托管内存片段具有相似的能力(内存片段内的内存分配、具名对象构建),但在managed_shared_memory, managed_mapped_file和managed_heap_memory, managed_external_file之间还是有一些显著的区别。
为看到托管堆内存和托管外部缓冲区类的效用,以下的例子展示了如何使用一个消息队列来序列化一整个采用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; }