一.内存池原理
平时我们直接使用的malloc、new、free、delete等API申请内存分配,但由于所申请的内存块的大小不确定,因此存在频繁使用会产生大量的内存片段、性能降低的缺点
内存池是内存分配方式,也称为固定大小块规划。 内存池将申请在实际使用内存之前分配一定量的相同大小的内存块作为备用。 需要新内存时,可以直接从内存池中分离部分内存块,如果内存块不足,则继续申请新内存,从而提高内存分配效率。
二、内存池源代码分析
1 .内存池数据结构源代码
首先,让我们看看内存池的主要数据结构。 结构位于include/linux/mempool.h文件中。
typedefstructmempool _ s { s pinlock _ t lock; //为防止多处理器并发而引入的锁定int min_nr; //elements数组的成员数int curr_nr; //当前elements数组中的空成员数void **elements; //用于存储内存成员的二维数组是elements[min_nr][内存对象长度]//与内存池和内核缓冲区组合使用的指针(该指针是此内存对象mempool_alloc_t *alloc; //内存分配函数mempool_free_t *free; //内存释放函数wait_queue_head_t wait; //任务等待队列} mempool_t; 2 .内存池创建函数源
中使用mempool_create ()创建内存池,使用mempool_destroy销毁内存池,使用mempool_alloc )创建内存和mempool_free )作为内存位于mm/mempool.c文件中的函数mempool_create :
mempool _ t * mempool _ create (intmin _ NR,mempool_alloc_t *alloc_fn,mempool_free_t *free_fn,voool ) /***********内存池对象参数: min_nr :分配给内存池的最小内存成员数alloc_fn :用户定义的内存分配函数(启用系统定义的函数) free 一般而言,缓存指针gfp_mask :存储器分配掩码node_id :存储器节点id * * * * * * * * * * * * * * */mempool _ t * * mem pol mempool_free_t *free_fn、void *pool_data、gfp_t gfp_mask、intnode //内存池对象中的内存pool=kz alloc _ data if (! pool )返回空值; //内存池if(mempool_init_node ) pool,min_nr,alloc_fn,free_fn,pool_data,gfp_mask,node_id }返回池; //返回内存池结构} export _ symbol (mempool _ create _ node ); mempool_create主要由mempool_create_node创建内存池,mempool_create_node首先分配内存池对象的内存,然后再分配mempool _ iii
int mempool _ init _ node (mempool _ t * pool,int min_nr,mempool_alloc_t *alloc_fn,mempool_free_t* pool-pool_data=pool_data; pool-alloc=alloc_fn; pool-free=free_fn; init _ wait queue _ head (池等待); //初始化队列//分配min_nr长度数组以存储申请后的对象的指针pool-elements=kmalloc _ array _ node (min _ NR,size
of(void *), gfp_mask, node_id);if (!pool->elements)return -ENOMEM;/* * First pre-allocate the guaranteed number of buffers. *///首先保证预分配的缓冲区数量while (pool->curr_nr < pool->min_nr) {void *element;//调用pool->alloc函数min_nr次element = pool->alloc(gfp_mask, pool->pool_data);if (unlikely(!element)) {//如果申请不到element,则直接销毁此内存池mempool_exit(pool);return -ENOMEM;}add_element(pool, element);//添加到elements指针数组中}return 0;}EXPORT_SYMBOL(mempool_init_node);3.内存池销毁函数源码
我们再看看mempool_destroy,mempool_destroy:
mempool_destroy也很简单,首先调用mempool_exit直接将elements存放的内存依个释放掉再释放elements指针数组结构体,后将mempool_t结构也释放掉。
4.内存池分配内存函数
现在我们看mempool_alloc()函数
当模块从此内存池中获取内存对象时,会调用此函数,此函数优先从伙伴系统或slab缓冲区获取需要的内存对象,当内存不足导致无法获取内存对象时,才会从内存池elements数组中获取,如果elements也没有空闲的内存对象,根据传入的分配标识进行相应的处理,最终会每5秒进行一次重新请求分配。
5.内存池释放内存函数
/ 内存池释放内存对象操作void mempool_free(void *element, mempool_t *pool){unsigned long flags;//传入的对象为空,则直接退出if (unlikely(element == NULL))return;/* * Paired with the wmb in mempool_alloc(). The preceding read is * for @element and the following @pool->curr_nr. This ensures * that the visible value of @pool->curr_nr is from after the * allocation of @element. This is necessary for fringe cases * where @element was passed to this task without going through * barriers. * * For example, assume @p is %NULL at the beginning and one task * performs "p = mempool_alloc(...);" while another task is doing * "while (!p) cpu_relax(); mempool_free(p, ...);". This function * may end up using curr_nr value which is from before allocation * of @p without the following rmb. */smp_rmb();//读内存屏障/* * For correctness, we need a test which is guaranteed to trigger * if curr_nr + #allocated == min_nr. Testing curr_nr < min_nr * without locking achieves that and refilling as soon as possible * is desirable. * * Because curr_nr visible here is always a value after the * allocation of @element, any task which decremented curr_nr below * min_nr is guaranteed to see curr_nr < min_nr unless curr_nr gets * incremented to min_nr afterwards. If curr_nr gets incremented * to min_nr after the allocation of @element, the elements * allocated after that are subject to the same guarantee. * * Waiters happen iff curr_nr is 0 and the above guarantee also * ensures that there will be frees which return elements to the * pool waking up the waiters. *///如果当前内存池中空闲的内存对象少于内存池中应当保存的内存对象的数量时,优先把释放的对象加入到内存池空闲数组中if (unlikely(pool->curr_nr < pool->min_nr)) {spin_lock_irqsave(&pool->lock, flags);if (likely(pool->curr_nr < pool->min_nr)) {add_element(pool, element);//将用户释放的element重新加到缓存而当中spin_unlock_irqrestore(&pool->lock, flags);wake_up(&pool->wait);//唤醒等待队列,目前已经有人释放内存,可以再次申请这个内存来使用return;}spin_unlock_irqrestore(&pool->lock, flags);}pool->free(element, pool->pool_data);//直接调用释放函数}EXPORT_SYMBOL(mempool_free);mempool_free将空闲内存对象释放到内存池中,当内存池中空闲对象不足时,优先将空闲内存对象放到elements数组中,把mempool填满,否则直接释放掉,让内存返回到伙伴系统或slab缓冲区中。