elr-memory-pool - 内存池实现


MIT
跨平台
C/C++

软件简介

简介

这是一款高效、灵活、跨平台的内存池实现。使用MIT
Licence发布,完全不排斥商业使用。它已经在许多生产环境中使用了。在该实现中内存被划分为节点(node)和切片(slice)。node一大块内存,slice是node上的小片内存,从内存池中申请的每一个内存都属于一个slice。每一个内存池实例里德slice都是一样大小的,所以这个内存池更像对象池。但是仍然可以基于该内存池实现一款更加灵活的可以从中申请不同尺寸的内存的内存池。

node链接成一个链表,可以使用的slice也链接成一个链表。当从内存池中申请内存时,首先检查是否有空闲的slice,如果有取出一个;如果没有,就检查最近申请的node里是否还有从未使用过的切片。如果最近申请的node里有从未使用过的slice,那么取出一个;如果没有将这个node添加到node链表的头部再申请一个新node并从中取出一个slice返回。释放内存时,仅仅需要将slice插入到空闲的slice链表头部。

这个内存池被组织为树状结构。当创建一个内存池时,可以为其指定父内存池,在调用elr_mpl_create时使用父内存池的指针作为第一个参数即可。当一个内存池被销毁时,它的子内存池也会被销毁。所以当一个内存池和它的子内存池不再使用时不必将所有的内存池一一销毁,仅仅为父内存池调用销毁接口即可。如果在创建内存池时不指定父内存池,那么一个全局的内存池就是它的父内存池。它是在第一次调用初始化内存池函数(elr_mpl_init)时被创建的。所有的内存池结构所占据的内存空间都来自于这个全局内存池。在最后一次调用终止化(elr_mpl_finalize)内存池时这个全局内存池被销毁。同时可以看出所有的内存池实例都是这个全局内存池的直接或者间接的子内存池。那么当elr_mpl_finalize被调用后所有的内存池实例也将被销毁。这将内存泄露的可能性降到了最低。

这个内存池也支持多线程。如果需要在多线程环境下使用它,就需要实现elr_mtx.h中定义的六个接口并且在编译时定义宏ELR_USE_THREAD。幸运的是实现它们非常简单,并且已经提供了一个windows平台下的实现。在提供windows平台下的实现时也考虑到了linux的兼容性。所以原子计数器类型和计数器类型(
counter(interger) type and counter value type
)被分别定义了。在windows平台下并没有对原子计数器类型单独定义,而是提供一个被volatile修饰的LONG类型。在LONG和volatile
LONG之间赋值是被允许的。在linux平台下原子计数器类型被定义为这样:typedef struct { volatile int counter; }
atomic_t; 。在int和atomic_t之间赋值是违法语法的。

这个内存池在生产环境中被证明是非常有效的。即使如此这个内存池也有很大的改良空间。在多线程环境下使用时,每一个内存池实例都拥有一个互斥体,很多情形下这不是必须的。所以这个内存池至少有两个地方可以改进。第一,减少对互斥体的消耗,这对嵌入式系统来说是非常有意义的。第二,扩展它使得它能够像appache的内存池一样可以从内存池实例中申请多种规则尺寸的内存块。

使用示例

#include <stdio.h>
#include <stdlib.h>
#include "elr_mpl.h"

int main()
{
    elr_mpl_t mypool = ELR_MPL_INITIALIZER;
    elr_mpl_t mysubpool = ELR_MPL_INITIALIZER;
    void*  mem = NULL; 
    int    len = 0;

    elr_mpl_init();

    mypool = elr_mpl_create(NULL,256);
    printf("%s\n","create a memory pool: mypool.");

    mysubpool = elr_mpl_create(&mypool,128);
    printf("%s\n","create a sub memory pool of mypool, name is mysubpool.");

    mem = elr_mpl_alloc(&mysubpool);
    printf("%s\n","alloc a memory block form mysubpool.");

    len = elr_mpl_size(mem);
    printf("the memory block size is %d.\n",len);

    elr_mpl_free(mem);
    printf("give back the memory block to mysubpool.\n",len);

    mem = elr_mpl_alloc(&mypool);
    printf("%s\n","alloc a memory block form mypool.");

    len = elr_mpl_size(mem);
    printf("the memory block size is %d.\n",len);

    elr_mpl_free(mem);
    printf("give back the memory block to mypool.\n",len);

    elr_mpl_destroy(&mypool);
    printf("destroy mypool.\n",len);

    printf("when mypool has destoryed, it`s sub pool, mysubpool, did %s destoryed.\n",
        elr_mpl_avail(&mysubpool) == 0?"also":"not");

    elr_mpl_finalize();

    getchar();
    return 0;
}