继承上一篇《ACE框架 基于共享内存的分配器设计》,本篇分析算法部分的设计。
ACE_Malloc_T模板定义了这样一个分配器组件
分配器组件聚合了三个功能组件:同步组件ACE_LOCK,内存块管理算法组件ACE_CB, 以及内存底层服务组件ACE_MEM_POOL_1。
内存底层服务组件ACE_MEM_POOL_1只提供向系统申请内存,并不参与分配器块管理。
分配器定义了4个功能核心算法的函数,分别是shared_malloc和shared_free(提供块分配管理),以及shared_find和shared_bind(提供命名发布)。这4个核心算法依赖算法组件ACE_CB。这个依赖并不是接口方式的依赖,而是结构上强偶合的依赖。ACE_CB组件必须实现算法中使用到的结构成员,并且理解算法中结构成员之间的关系。
ACE_CB必须提供块头结构定义,以及一个空闲链表和一个名称服务管理链表。
分配器核算法shared_malloct和shared_free维护空闲链表,而shared_find和shared_bind维护名称服务管理链表。
为什么ACE_CB是结构上的强偶合依赖,而不是接口上依赖。因为ACE_CB组件上的资源必须储存在由ACE_MEM_POOL_1组件分配的某种性质的内存上。
一:下面来看分配器为我们提供了如何块分配管理算法。
1. 块必须有一个块头可以进行链表维护,块的向下cast到用户的使用区返回给用户使用,分配器可以通过块的用户使用区upcast到块头,从而进行块的维护。
2. 块分配按块头的尺寸的整数倍进行拆分,块头必须对齐一定的字长倍数。这样在分配和释放管理中,有利于进行拆分和合并。因此在ACE_CB组件提供的块头定义中,块头使用的size_成员,它的计量单位并不是byte,而是块头尺寸的倍数。另外,在分配时,用户申请的大小都会被对齐这个倍数,所以通常会返回一个rounded_bytes。
3. 空闲链表按地址升序链接所有空闲块,空闲块被分配后其块头的next_block_成员必须指向自身,因为在共享内存的分配器中,分配出去的块必须通过使用ACE_Based_Pointer的next_block_指明本身的偏移量,在释放时可以被不同进程重新获知块所在共享内存空间的位置。
4. 当空闲链表上的空闲块不能满足用户的分配要求时,向ACE_MEM_POOL_1组件索取整体的大块。
5. 每次分配都遍历升序的空闲链表,找满足(不小于)用户分配要求的第一个空闲块进行拆分。在拆分时,从块的底部开始进行分裂,这样可以避免一次脱链和重新链入。
6. 每次释放时都遍历升序的空闲链表,按升序找插入位置,在插入时要考虑将相粼的块进行合并。
分配算法并不提供堆的方式进行管理,当用户要求分配的小块时,尽管空闲链表中有最接近要求的空闲块,也会因为空闲块的地址顺序而从小地址的大块中进行拆分。可以预想到在使用这个分配器进行频繁的小块分配和释放,即应用在频繁的短期小块使用的场合中,是不利的。
二:ACE_PI_Control_Block初始化算法。
1. 相同路径的分配器的ACE_PI_Control_Block只可以进行一次初始化,这是ACE_MMAP_Memory_Pool帮助决定的。ACE_MMAP_Memory_Pool在进行内存映射时可以通过创建映射的失败来判断,分配器已经进行过初始化打开。
2. ACE_PI_Control_Block必须固定在ACE_MMAP_Memory_Pool映射内存的开始位置。
3. 空闲块链表是一个环链表,必须为其指定一个链表头。