三、内核缓存区和内存池的初始化
rpc_buffer_slabp = kmem_cache_create("rpc_buffers",
RPC_BUFFER_MAXSIZE,
0, SLAB_HWCACHE_ALIGN,
NULL, NULL);
调用kmem_cache_create函数从系统缓存区cache_cache中获取长度为RPC_BUFFER_MAXSIZE的缓存区大小的内存,作为rpc_buffer使用的缓存区。而以后对rpc操作的所有数据结构内存都是从这块缓存区申请,这是linux的slab技术的要点,而内存池也是基于这段缓存区进行的操作。
一旦rpc服务申请到了一个缓存区rpc_buffer_slabp以后,就可以创建一个内存池来管理这个缓存区了:
rpc_buffer_mempool = mempool_create(RPC_BUFFER_POOLSIZE,
mempool_alloc_slab,
mempool_free_slab,
rpc_buffer_slabp);
mempool_create函数就是内存池创建函数,负责为一类内存对象构造一个内存池,传递的参数包括,内存池大小,定制的内存分配函数,定制的内存析构函数,这个对象的缓存区指针。下面是mempool_create函数的具体实现:
/**
* mempool_create – 创建一个内存池对象
* @min_nr: 为内存池分配的最小内存成员数量
* @alloc_fn: 用户自定义内存分配函数
* @free_fn: 用户自定义内存释放函数
* @pool_data: 根据用户自定义内存分配函数所提供的可选私有数据,一般是缓存区指针
*/
mempool_t * mempool_create(int min_nr, mempool_alloc_t *alloc_fn,
mempool_free_t *free_fn, void *pool_data)
{
mempool_t *pool;
/*为内存池对象分配内存*/
pool = kmalloc(sizeof(*pool), GFP_KERNEL);
if (!pool)
return NULL;
memset(pool, 0, sizeof(*pool));
/*根据内存池的最小长度为elements数组分配内存*/
pool->elements = kmalloc(min_nr * sizeof(void *), GFP_KERNEL);
if (!pool->elements) {
kfree(pool);
return NULL;
}
spin_lock_init(&pool->lock);
/*初始化内存池的相关参数*/
pool->min_nr = min_nr;
pool->pool_data = pool_data;
init_waitqueue_head(&pool->wait);
pool->alloc = alloc_fn;
pool->free = free_fn;
<wrapblock><shape id="_x0000_s1026" style="MARGIN-TOP: 31.2pt; Z-INDEX: 1; LEFT: 0px; MARGIN-LEFT: 54pt; WIDTH: 294.8pt; POSITION: absolute; HEIGHT: 191.25pt; TEXT-ALIGN: left" type="#_x0000_t75"><imagedata src="file:///C:/DOCUME~1/JOSHUA~1.GES/LOCALS~1/Temp/msohtml1/07/clip_image001.emz" o:title="" croptop="22382f"></imagedata><wrap type="topAndBottom"></wrap></shape></wrapblock>/*首先为内存池预先分配min_nr个element对象,这些对象就是为了存储相应类型的内存对象的:
*/
while (pool->curr_nr < pool->min_nr) {
void *element;
element = pool->alloc(GFP_KERNEL, pool->pool_data);
if (unlikely(!element)) {
free_pool(pool);
return NULL;
}
/*将刚刚申请到的内存挂到elements数组的相应位置上,并修改curr_nr的值*/
add_element(pool, element);
}
/*若成功创建内存池,则返回内存池对象的指针,这样就可以利用mempool_alloc和mempool_free访问内存池了。*/
return pool;
}
四、内存池的使用
如果需要使用已经创建的内存池,则需要调用mempool_alloc从内存池中申请内存以及调用mempool_free将用完的内存还给内存池。
void * mempool_alloc(mempool_t *pool, int gfp_mask)
{
void *element;
unsigned long flags;
DEFINE_WAIT(wait);
int gfp_nowait = gfp_mask & ~(__GFP_WAIT | __GFP_IO);
repeat_alloc:
/*这里存在一些不明白的地方,先将用户传递进来的gfp掩码标志去掉__GFP_WAIT 和 __GFP_IO 两个标志,试图调用用户自定义分配函数从缓存区申请一个内存对象,而不是首先从内存池从分配,如果申请不到,再从内存池中分配。*/
element = pool->alloc(gfp_nowait|__GFP_NOWARN, pool->pool_data);
if (likely(element != NULL))
return element;
/*如果池中的成员(空闲)的数量低于满时的一半时,需要额外从系统中申请内存,而不是从内存池中申请了。但是如果这段内存使用完了,则调用mempool_free将其存放到内存池中,下次使用就不再申请了。*/
mb();
if ((gfp_mask & __GFP_FS) && (gfp_mask != gfp_nowait) &&
(pool->curr_nr <= pool->min_nr/2)) {
element = pool->alloc(gfp_mask, pool->pool_data);
if (likely(element != NULL))
return element;
}
spin_lock_irqsave(&pool->lock, flags);
/*如果当前内存池不为空,则从池中获取一个内存对象,返回给申请者*/
if (likely(pool->curr_nr)) {
element = remove_element(pool);
spin_unlock_irqrestore(&pool->lock, flags);
return element;
}
spin_unlock_irqrestore(&pool->lock, flags);
/* We must not sleep in the GFP_ATOMIC case */
if (!(gfp_mask & __GFP_WAIT))
return NULL;
/*下面一部分应该和内核调度有关,所以暂时不看了*/
prepare_to_wait(&pool->wait, &wait, TASK_UNINTERRUPTIBLE);
mb();
if (!pool->curr_nr)
io_schedule();
finish_wait(&pool->wait, &wait);
goto repeat_alloc;
}
如果申请者调用mempool_free准备释放内存,实际上是将内存对象重新放到内存池中。源码实现如下:
void mempool_free(void *element, mempool_t *pool)
{
unsigned long flags;
mb();
/*如果当前内存池已经满,则直接调用用户内存释放函数将内存还给系统*/
if (pool->curr_nr < pool->min_nr) {
spin_lock_irqsave(&pool->lock, flags);
if (pool->curr_nr < pool->min_nr) {
/*如果内存池还有剩余的空间,则将内存对象放入池中,唤醒等待队列*/
add_element(pool, element);
spin_unlock_irqrestore(&pool->lock, flags);
wake_up(&pool->wait);
return;
}
spin_unlock_irqrestore(&pool->lock, flags);
}
pool->free(element, pool->pool_data);
}
这个函数十分简单,没有什么过多的分析了。
通过上面的分析,我们发现Linux内核的内存池实现相当简单。而C++STL中,实现了二级分配机制,初始化时将内存池按照内存的大小分成数个级别(每个级别均是8字节的整数倍,一般是8,16,24,…,128字节),每个级别都预先分配了20块内存。二级分配机制的基本思想是:如果用户申请的内存大于我们预定义的级别,则直接调用malloc从堆中分配内存,而如果申请的内存大小在128字节以内,则从最相近的内存大小中申请,例如申请的内存是10字节,则可以从16字节的组中取出一块交给申请者,如果该组的内存储量(初始是20)小于一定的值,就会根据一个算法(成为refill算法),再次从堆中申请一部分内存加入内存池,保证池中有一定量的内存可用。
五、内存池实现总结
而Linux的内存池实际上是与特定内存对象相关联的,每一种内存对象(例如task_struct)都有其特定的大小以及初始化方法,这个与STL的分级有点相似,但是内核主要还是根据实际的对象的大小来确定池中对象的大小。
内核内存池初始时从缓存区申请一定量的内存块,需要使用时从池中顺序查找空闲内存块并返回给申请者。回收时也是直接将内存插入池中,如果池已经满,则直接释放。内存池没有动态增加大小的能力,如果内存池中的内存消耗殆尽,则只能直接从缓存区申请内存,内存池的容量不会随着使用量的增加而增加。
相关推荐
Linux内核内存池实现研究Linux内核内存池实现研究Linux内核内存池实现研究Linux内核内存池实现研究Linux内核内存池实现研究Linux内核内存池实现研究Linux内核内存池实现研究
适用于linux服务器开发,提升性能,希望对你有帮助!
linux 内核 内存泄露检测 linux 内核 内存泄露检测 linux 内核 内存泄露检测 linux 内核 内存泄露检测 linux 内核 内存泄露检测 linux 内核 内存泄露检测 linux 内核 内存泄露检测
Linux内核内存管理,很全的内存管理讲解,自己手动整理的,绝对原创。
第 7卷第 12期2007年 6月Linux内核内存池实现研究(西北工业大学 , 西安 710065)回顾了 Linux内核内存管理发展历程, 比较了早期的 L
linux开发者介绍linux内存管理的机制,图文并茂,细致入里
linux内核内存管理技术
linux内核内存管理图解
linux内核管理linux内核管理linux内核管理linux内核管理linux内核管理
Linux 4.4.0内核源码分析TCP实现
Linux内核的内存管理探秘之四 虚拟内存的管理.pdf
此Linux内核学习资料包中有Linux内核--网络栈实现分析(二)--数据包的传递过程(上).pdf Linux内核--网络栈实现分析(三)--驱动程序层+链路层(上).pdf Linux内核--网络栈实现分析(四)--网络层之IP协议(上)....
深入理解Linux内核 + Linux内核设计与实现,绝对完整,我最近也在学,建议先学Linux内核设计与实现,对Linux内核有一个大体的认识,在看深入理解Linux内核,要舍得花时间。
Linux 4.4.0 内核源码分析 TCP实现 - Linux4.40版本的TCP/IP协议栈源码分析
Linux内核中内存池的实现及应用.pdf
Linux内核缓冲区管理 Linux内核缓冲区管理 Linux内核缓冲区管理
linux 内核 内存泄露检测 linux 内核 内存泄露检测 linux 内核 内存泄露检测 linux 内核 内存泄露检测 linux 内核 内存泄露检测
《Linux内核网络栈源代码情景分析》主要对Linux1.2.13内核协议栈的全部源代码做了详细的分析,该版本所有代码都在一个文件夹中,每种协议的实现都只有一个文件与之对应,分析该版本源代码可以方便读者迅速掌握Linux...
linux内核设计说明,Linux内核设计与实现(第三版中文高清带目录)
摘要:基于对Linux操作系统内存管理机制、算法和模式的分析,详细解读Linux内核2.6版本中有关内存池的定义内涵, 明确内存池的创建方法和调用原理, 并给出