在C语言中动态内存的实现主要有两种方法:
1、通常采用malloc和free函数实现内存的分配和释放,这也是常用的方法,但是这种方式会导致内存碎片的产生,特别是频繁的分配会导致大量的内存碎片产生。
2、内存池的使用,内存池的基本实现方法主要是采用链表的形式将静态的内存空间动态的链接起来,这样就能避免内存碎片的产生。因此具体的链表组织方式就是我们在实际中应该考虑的。
内存池是一系列固定大小的内存空间,每一个内存池主要包括很多内存单元(具体的存储区域)和内存控制单元(控制对应的内存单元),每一个内存单元大小相同,但是具体的大小依据需要设计。而控制单元主要是实现每一个子内存空间的控制,因此采用一一对应的方式,每一个具体的子内存空间分配一个控制单元。静态的内存单元本来可以采用数组的方式进行控制,但是数组要求相同的数据类型,而且不便于确定那些内存单元是可以处理,那些是不能进行处理的,而链表的组织方式则能够比较清晰的确定空闲的区域,减少了判定的具体时间,对于不同的下标得计算的时间是不同的,而采用链表的方式则能够实现相同的速度访问,由于不是通过一一比对的方式实现的,采用从空闲链表中弹出空闲子内存空间的方式就能保证所有的访问时间是相同的。
内存池基本的结构如图所示:
根据上面的图是内存池实现的基本框架图,可以将内存池设计为三个部分,主要是mem_pool_struct结构体,mem_pool_node_struct结构体以及内存空间(buffer)。mem_pool_struct主要包含一个内存池的基本信息。而mem_pool_node_struct则主要控制对应的内存空间buffer。而buffer则是具体的子空间。空闲链表、使用链表则主要是为了实现对多个内存池的控制。基本的结构体定义如下:
/******************************************************
*
*主要完成基本的内存控制
*该文档主要完成结构体和接口的设计
*
*******************************************************/
#ifndef __MEM_POOL_H_H_
#define __MEM_POOL_H_H_
#define NAME_MAX_LENGTH 31
/*采用宏定义实现内存池的buffer和控制器的创建*/
#define MEM_POOL_DECLARE(_node_name,_buffer_name,_type,count)\
static mem_pool_node_t _node_name[count]\
static _type _buffer_name[count]
/*内存池节点控制单元*/
typedef struct {
/*内存块控制器的节点*/
dll_node_t node;
/*指向的内存块起始地址*/
address_t addr;
/*表示该内存块是否被使用*/
bool is_used;
}mem_pool_node_t;
/*内存池结构体*/
typedef struct
{
/*用来链接不同的内存池*/
dll_node_t node;
/*魔数,用来检测是否有效*/
magic_number_t magic_number;
/*内存池名*/
char name[NAME_MAX_LENGTH + 1];
/*内存的开始地址*/
address_t addr_start;
/*内存的结束地址*/
address_t addr_end;
/*子内存块的大小*/
size_t buffer_size;
/*子内存的个数*/
size_t buffer_count;
/*节点指针,便于操作后面而定义的*/
mem_pool_node_t *mpool_head;
/*用来链接空闲的子内存区间,也就是与mem_pool_node_t中的node链接*/
dll_t free_buffer;
/*统计信息*/
statistic_t stats_nobuf;
}mem_pool_t,*mem_pool_handle_t;
/*防止被C++编译器编译*/
#ifdef __cplusplus
extern "C"
{
#endif
error_t mem_pool_create(const char _name[],mem_pool_handle_t *handle,
void *node/*控制块的指针*/,void *buffer,size_t buffer_size,
size_t buffer_count);
error_t mem_pool_delete(mem_pool_handle_t handle);
void * mem_pool_buffer_alloc(mem_pool_handle_t handle);
error_t mem_pool_buffer_free(mem_pool_handle_t handle,void *buffer);
#ifdef __cplusplus
}
#endif
#endif
由于其中该头文件主要实现了几个重要的结构体,分别是mem_pool_t和mem_pool_node_t,分别是内存池结构体和内存池子空间控制块结构体。
/**********************************************************
*
*文档用来实现内存池的基本实现
*
* *******************************************************/
#include"mem_pool_h"
#define MPOOL_MAX_NUMBER 8
#define MPOOL_MAX_BUFFER_COUNT 32
/*创建8个内存池*/
static mem_pool_t g_mem_pool_t[MPOOL_MAX_NUMBER];
/*空闲链表,链接空闲的内存池*/
static dll_t g_free_link;
/*使用链表,链接正在使用的内存池*/
static dll_t g_used_link;
/**********************************************************
* 该函数主要完成将上面创建的八个内存池,
* 分配通过mem_pool_t中的node连接到空闲
* 链表中。
**********************************************************/
static void mem_pool_init()
{
/*具体的依据链表提供的操作完成*/
}
/**********************************************************
*该函数主要完成内存池的创建,其中的参数主要包含
* _name是内存池名,
* handle是内存池结构体指针的指针
* node是子内存控制块数组的指针
* buffer是自内存块数组的指针
* 以上两个参数主要通过MEM_POOL_DECLARE实现
* buffer_size是指子内存空间的大小
* buffer_count是值子空间的个数
*********************************************************/
error_t mem_pool_create(const char _name[],mem_pool_handle_t *handle,
void *node/*控制块的指针*/,void *buffer,size_t buffer_size,
size_t buffer_count)
{
/*
* 主要完成mem_pool_t结构体的填充,不需要采用malloc创建,
* 而是通过链表弹出空间的方式实现,将内存池对象连接到使用链表
*/
/*
* 同时将各个子内存空间的控制块节点添加到mem_pool_t中的free_buffer中
*/
}
/**********************************************************
*该函数主要是完成内存池的删除,实质上也就是完成mem_pool_t
*结构体中参数的修改,但是需要注意的是不能删除正在使用的内存池
* *******************************************************/
error_t mem_pool_delete(mem_pool_handle_t handle)
{
/*
*完成将内存池对象从使用链表中移除,并将其连接到空闲链表
*确保所有的子内存空间没有被使用
* */
}
/*************************************************************
*该函数主要是依据内存池中的链表free_buffer中找出空闲的子内存空间
*基本的原理就是依据free_buffer找到子内存空间的空闲块,然后依据
*mem_pool_node_t中的addr找到内存空间的指针,也就得到内存空间
*************************************************************/
void * mem_pool_buffer_alloc(mem_pool_handle_t handle)
{
/*从free_buffer中弹出空闲的子内存空间*/
/*通过mem_pool_node_t中的addr找到子内存空间,并将状态修改为使用*/
}
/*************************************************************
* 该函数主要通过buffer的地址找到对应的子内存控制块,
* 将mem_pool_node中的is_used设置为0即可
* ***********************************************************/
error_t mem_pool_buffer_free(mem_pool_handle_t handle,void *buffer)
{
/*通过buffer找到子内存空间控制器对应的下标,
* 然后将mem_pool_node设置为没有使用
* 最重要的是将该控制器节点的node压入mem_pool_t中的free_buffer中
* */
}
由于关于链表的操作没有实现,本文没有实现具体的操作,但是各个函数的实现要点都已经给出。
内存池没有反复的分配和释放,这样就能较好的避免内存碎片的产生。其实只要明白基本的模块就能比较好的实现内存池。即内存池结构体(mem_pool_t),结构体中将链表节点作为第一个元素主要是为了快速的强制类型转换得到新的数据类型指针。比如mem_pool_t的node可以从空闲链表中弹出,然后强制类型转换既可以得到mem_pool_t的指针。内存控制单元(mem_pool_t)的实现。
实质上所有的操作都是基于链表的操作,通过内存池结构体中的链表将所有的子空间控制单元链接起来,实现了一个内存池的操作。而外部的链表则实现了不同内存池的操作。
其中的采用数组的方式进行分配空间主要是为了使后期的操作简单,能够依据数组的一些特性快速的实现内存池空间的管理。