Linux 内存池源码浅析

来源:
导读 大家好,我是本期栏目编辑小友,现在为大家讲解Linux 内存池源码浅析问题。 Memery Pool技术是在实际使用内存之前,申请分配一定数量的

大家好,我是本期栏目编辑小友,现在为大家讲解Linux 内存池源码浅析问题。

Memery Pool技术是在实际使用内存之前,申请分配一定数量的大小相同的内存块(一般情况下)备用。当有新的内存需求时,一部分内存块会从内存池中分离出来,如果内存块不够,继续申请新内存。这样做的一个显著优点是尽可能避免了内存碎片,提高了内存分配的效率。

它不仅广泛应用于用户模式的应用程序,也广泛应用于不允许内存分配失败的Linux内核。作为在这些情况下确保分配的一种方式,内核开发人员创建了一个称为内存池(或“mempool”)的抽象。内核中的内存池实际上只相当于备份缓存。它会尽力保留一个空闲内存列表以备紧急使用,并在一般有内存需求时直接从公共内存中分配。虽然这种方式有点占用内存的嫌疑,但是可以从根本上保证关键应用在内存不足的情况下仍然可以成功申请内存。

让我们看看内核内存池的源代码。内核内存池的源代码在,实现起来非常简洁。描述内存池结构的mempool_t在头文件中定义。结构描述如下:

typedef struct mempool _ s { spin lock _ t lock;/*保护内存池的自旋锁*/int min _ NR;/*内存池中可分配元素的最小数量*/int curr _ NR;/*剩余待分配的元素数量*/void * *元素;/*指向元素池的指针*/void * pool _ data;/*内存源,即池中的元素实际分配的位置*/mempool _ alloc _ t * alloc;/*分配元素的方法*/mempool _ free _ t * free;/*回收元素的方法*/wait _ queue _ head _ t wait;/*阻塞的等待队列*/} mempool _ t;12345678910 typedef struct mempool _ s { spin lock _ t }锁;/*保护内存池的自旋锁*/int min _ NR;/*内存池中可分配元素的最小数量*/int curr _ NR;/*剩余待分配的元素数量*/void * *元素;/*指向元素池的指针*/void * pool _ data;/*内存源,即池中的元素实际分配的位置*/mempool _ alloc _ t * alloc;/*分配元素的方法*/mempool _ free _ t * free;/*回收元素的方法*/wait _ queue _ head _ t wait;/*阻塞的等待队列*/} mempool _ t;内存池创建函数mempool_create的函数原型如下:

mempool _ t * mempool _ create(int min _ NR,mempool_alloc_t *alloc_fn,mempool_free_t *free_fn,void * pool_data){ return mempool _ create _ node(min _ NR,alloc_fn,free_fn,pool _ data,-1);} 12345 EMP pool _ t * mempool _ create(int min _ NR,mempool_alloc_t *alloc_fn,mempool_free_t *free_fn,void * pool_data){ return mempool _ create _ node(min _ NR,alloc_fn,free_fn,pool _ data,-1);}函数原型指定了内存池可以容纳的元素数量、申请元素的方法、释放元素的方法以及可选的内存源(通常是缓存)。创建内存池对象后,将自动调用alloc方法,从pool_data中分配min_nr元素来填充内存池。

内存释放函数mempool_destory函数的原型非常简单,应该猜到元素对象依次从池中移除,然后释放到pool_data,最后释放到池对象,如下所示:

void mempool _ destroy(mempool _ t * pool){ while(pool-curr _ NR){ void * element=remove _ element(pool);无池(元素,池-池_数据);} kfree(池元素);kfree(游泳池);} 123456789 void mempool _ destroy(mempool _ t * pool){ while(pool-curr _ NR){ void * element=remove _ element(pool);无池(元素,池-池_数据);}kfree(池元素);kfree(游泳池);}值得注意的是内存池分配和回收对象的功能:mempool_alloc和mempool_free。Mempool_alloc用于从指定的内存池中申请/获取对象和函数原型。

如下:

       void * mempool_alloc(mempool_t *pool, gfp_t gfp_mask){...... element = pool->alloc(gfp_temp, 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); /* paired with rmb in mempool_free(), read comment there */ smp_wmb(); return element; }...... }1234567891011121314151617void * mempool_alloc(mempool_t *pool, gfp_t gfp_mask){......    element = pool->alloc(gfp_temp, 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);        /* paired with rmb in mempool_free(), read comment there */        smp_wmb();        return element;    }......    }

函数先是从pool_data中申请元素对象,当从pool_data无法成功申请到时,才会从池中提取对象使用,因此可以发现内核内存池mempool其实是一种后备池,在内存紧张的情况下才会真正从池中获取,这样也就能保证在极端情况下申请对象的成功率,单也不一定总是会成功,因为内存池的大小毕竟是有限的,如果内存池中的对象也用完了,那么进程就只能进入睡眠,也就是被加入到pool->wait的等待队列,等待内存池中有可用的对象时被唤醒,重新尝试从池中申请元素:

       init_wait(&wait);prepare_to_wait(&pool->wait, &wait, TASK_UNINTERRUPTIBLE);spin_unlock_irqrestore(&pool->lock, flags);io_schedule_TImeout(5*HZ);finish_wait(&pool->wait, &wait);12345init_wait(&wait);prepare_to_wait(&pool->wait, &wait, TASK_UNINTERRUPTIBLE);spin_unlock_irqrestore(&pool->lock, flags);io_schedule_TImeout(5*HZ);finish_wait(&pool->wait, &wait);

池回收对象的函数mempool_free的原型如下:

       void mempool_free(void *element, mempool_t *pool){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);}1234567891011121314void mempool_free(void *element, mempool_t *pool){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);}

其实原则跟mempool_alloc是对应的,释放对象时先看池中的可用元素是否充足(pool->curr_nr == pool->min_nr),如果不是则将元素对象释放回池中,否则将元素对象还给pool->pool_data。

此外mempool也提供或者说指定了几对alloc/free函数,及在mempool_create创建池时必须指定的alloc和free函数,分别适用于不同大小或者类型的元素的内存池,具体如下:

       void *mempool_alloc_slab(gfp_t gfp_mask, void *pool_data){ struct kmem_cache *mem = pool_data; return kmem_cache_alloc(mem, gfp_mask);}void mempool_free_slab(void *element, void *pool_data){ struct kmem_cache *mem = pool_data; kmem_cache_free(mem, element);}void *mempool_kmalloc(gfp_t gfp_mask, void *pool_data){ size_t size = (size_t)pool_data; return kmalloc(size, gfp_mask);}void mempool_kfree(void *element, void *pool_data){ kfree(element);}void *mempool_alloc_pages(gfp_t gfp_mask, void *pool_data){ int order = (int)(long)pool_data; return alloc_pages(gfp_mask, order);}void mempool_free_pages(void *element, void *pool_data){ int order = (int)(long)pool_data; __free_pages(element, order);}12345678910111213141516171819202122232425262728293031void *mempool_alloc_slab(gfp_t gfp_mask, void *pool_data){    struct kmem_cache *mem = pool_data;    return kmem_cache_alloc(mem, gfp_mask);}void mempool_free_slab(void *element, void *pool_data){    struct kmem_cache *mem = pool_data;    kmem_cache_free(mem, element);} void *mempool_kmalloc(gfp_t gfp_mask, void *pool_data){    size_t size = (size_t)pool_data;    return kmalloc(size, gfp_mask);}void mempool_kfree(void *element, void *pool_data){    kfree(element);} void *mempool_alloc_pages(gfp_t gfp_mask, void *pool_data){    int order = (int)(long)pool_data;    return alloc_pages(gfp_mask, order);}void mempool_free_pages(void *element, void *pool_data){    int order = (int)(long)pool_data;    __free_pages(element, order);}

总体上来讲mempool的实现很简约,但是不简单,而且非常轻便易用,这也是内核奥妙之所在。

标签:

版权声明:转载此文是出于传递更多信息之目的。若有来源标注错误或侵犯了您的合法权益,请作者持权属证明与本网联系,我们将及时更正、删除,谢谢您的支持与理解。