对于使用Boost进程间共享内存的以下程序,
#include <iostream>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/list.hpp>
#include <iostream>
#define SHARED_MEMORY_NAME "SO12439099-MySharedMemory"
#define DATAOUTPUT "OutputFromObject"
#define INITIAL_MEM 650000
#define STATE_MATRIX_SIZE 4
using namespace std;
namespace bip = boost::interprocess;
class SharedObject
{
public:
unsigned int tNumber;
bool pRcvdFlag;
bool sRcvdFlag;
unsigned long lTimeStamp;
};
typedef bip::allocator<SharedObject, bip::managed_shared_memory::segment_manager> ShmemAllocator;
typedef bip::list<SharedObject, ShmemAllocator> SharedMemData;
int main()
{
bip::managed_shared_memory* seg;
SharedMemData *sharedMemOutputList;
bip::shared_memory_object::remove(DATAOUTPUT);
seg = new bip::managed_shared_memory(bip::create_only, DATAOUTPUT, INITIAL_MEM);
const ShmemAllocator alloc_inst(seg->get_segment_manager());
sharedMemOutputList = seg->construct<SharedMemData>("TrackOutput")(alloc_inst);
std::size_t beforeAllocation = seg->get_free_memory();
std::cout<<"\nBefore allocation = "<< beforeAllocation <<"\n";
SharedObject temp;
sharedMemOutputList->push_back(temp);
std::size_t afterAllocation = seg->get_free_memory();
std::cout<<"After allocation = "<< afterAllocation <<"\n";
std::cout<<"Difference = "<< beforeAllocation - afterAllocation <<"\n";
std::cout<<"Size of SharedObject = "<< sizeof(SharedObject) <<"\n";
std::cout<<"Size of SharedObject's temp instance = "<< sizeof(temp) <<"\n";
seg->destroy<SharedMemData>("TrackOutput");
delete seg;
}//main
输出为:
Before allocation = 649680
After allocation = 649632
Difference = 48
Size of SharedObject = 16
Size of SharedObject's temp instance = 16
如果SharedObject
及其实例的大小为16字节,那么分配的差异如何才能为48?即使padding已经自动完成,但仍然太多而无法考虑尺寸的3倍(对于较大的结构,它会达到尺寸的1.33倍)。
因此,我无法可靠地分配和动态增长共享内存。如果SharedObject
包含一个动态增长的列表,那么可能会增加空间分配的不确定性。
如何安全地处理这些情况?
ps:要运行该程序,您必须链接pthread
库和librt.so
。
更新:
这是我在多次运行的tabulated值时获得的内存使用模式(memory increase
列基本上是memory used
列的当前行减去{的前一行{1}}):
╔═════════════╦════════════════╦═══════════════ ══╗
║存储器使用║结构尺寸║存储器增加║
╠═════════════╬════════════════╬═════════════════╣
║48║1║║
║48║4║0║
║48║8║0║
║48║16║0║
║64║32║16║
║64║40║0║
║80║48║16║
║96║64║32║
║160║128║64║
║288║256║128║
║416║384║128║
║544║512║128║
║║800 768 256║║
║║1056 1024║║256
╚═════════════╩════════════════╩═════════════════╝
重要提示:上表仅适用于共享内存memory used column
。对于list
,(使用的内存,结构大小)值为=(48,1),(48,8),(48,16),(48,32),(80,64),(80 ,72),(112,96),(128,120),(176,168),(272,264),(544,528)。
因此,其他容器需要不同的内存计算公式。
答案 0 :(得分:4)
请记住,任何通用分配机制都有一个有效负载,以便存储有关如何释放该内存的信息,如何将该缓冲区与相邻缓冲区合并等。这种情况发生在您的系统malloc中(通常,8-16额外每个分配的字节加上额外的对齐)。共享内存中的内存分配器的开销为4-8字节(在32位系统中,在64位系统中为8-16)
然后库需要存储对象的数量,以便在调用&#34; destroy_ptr(ptr)&#34;时调用析构函数。 (您可以分配数组,因此您需要知道应该调用多少个析构函数)。并且您已经进行了命名分配,因此库需要将该字符串存储在共享内存中,并需要一些元数据来查找它(指向字符串的指针,也许这是一个&#34;命名分配&#34;以及不是&#34;匿名&#34;或&#34;实例分配)。
16个字节的数据+ 8个字节来自内存分配器+ 8个字节来存储指针+元数据到名称+ 12个字节来自字符串&#34; TrackOutput&#34; (包括null-end)加上8个字节的对齐,你得到48个字节。
每次分配的开销几乎是不变的。因此,常数因子1.33仅适用于小分配。如果你分配一个字节,你会得到更糟糕的因素,就像你从堆中分配一个char一样。
如果没有可用内存来存储新对象,则库会抛出异常,您可以捕获它并尝试创建新的托管共享内存。请注意,内存会因分配和重新分配而碎片化,因此即使共享内存中有空闲字节,托管共享内存也无法为您的请求提供服务,因为没有足够大的连续内存来实现它。共享内存无法自动扩展,因此与堆内存相比,碎片问题要大得多。
您无法动态增长共享内存,因为其他进程可能会与其连接,并且会在尝试访问未映射的页面时崩溃。唯一的选择是预先分配一个共享内存,这将足以添加一个常量填充因子或分配一个新的托管共享内存并通知所有其他读者/编写者(可能使用原始共享内存中的预分配结构)新元素将去在新的托管共享内存中。
答案 1 :(得分:1)
请参阅此处的分析和比较:Bad alloc is thrown
长话短说:基于节点的容器在这里不是很好。
考虑使用池分配器 - 是的,它以这种方式堆叠分配器有点笨拙,但它确实消除了节点分配上的大量开销。
您还可以考虑使用顺序存储,然后使用Boost Intrusive List容器“”。 Boost Intrusive与interprocess::offset_ptr
一起使用,因此您可以将其与托管内存段一起使用。