首页 > 编程知识 正文

深入理解linux内存管理(python释放变量内存)

时间:2023-05-04 17:34:42 阅读:73964 作者:292

一.内存池原理

平时我们直接使用的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:

//销毁一个内存池void mempool_destroy(mempool_t *pool){if (unlikely(!pool))return;mempool_exit(pool);//释放内存池中的内存块kfree(pool);//释放内存池结构体}EXPORT_SYMBOL(mempool_destroy);void mempool_exit(mempool_t *pool){while (pool->curr_nr) {void *element = remove_element(pool);//把elements指针数组中的内存移除pool->free(element, pool->pool_data);//释放elements数组中的所有对象}kfree(pool->elements);//销毁elements指针数组pool->elements = NULL;}EXPORT_SYMBOL(mempool_exit);

mempool_destroy也很简单,首先调用mempool_exit直接将elements存放的内存依个释放掉再释放elements指针数组结构体,后将mempool_t结构也释放掉。

4.内存池分配内存函数
现在我们看mempool_alloc()函数

//内存池分配对象void *mempool_alloc(mempool_t *pool, gfp_t gfp_mask){void *element;unsigned long flags;wait_queue_entry_t wait;gfp_t gfp_temp;//形参gfp_mask中不能包含_GFP_ZEROVM_WARN_ON_ONCE(gfp_mask & __GFP_ZERO);//如果有__GFP_WAIT标志,则会先阻塞,切换进程//#define might_sleep_if(cond) do { if (cond) might_sleep(); } while (0)might_sleep_if(gfp_mask & __GFP_DIRECT_RECLAIM);gfp_mask |= __GFP_NOMEMALLOC;//不使用预留内存gfp_mask |= __GFP_NORETRY;//分配页时如果失败则返回,不进行重试gfp_mask |= __GFP_NOWARN;//分配失败不提供警告//gfp_mask只保留__GFP_DIRECT_RECLAIM和__GFP_IO标志gfp_temp = gfp_mask & ~(__GFP_DIRECT_RECLAIM|__GFP_IO);repeat_alloc://使用内存池中的alloc函数进行分配对象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();//写内存屏障,保证之前的写操作已经完成/* * Update the allocation stack trace as this is more useful * for debugging. */kmemleak_update_trace(element);//用于debugreturn element;}/* * We use gfp mask w/o direct reclaim or IO for the first round. If * alloc failed with that and @pool was empty, retry immediately. *///这里是内存池中也没有空闲内存对象的时候进行的操作//如果gfp_temp != gfp_maskif (gfp_temp != gfp_mask) {spin_unlock_irqrestore(&pool->lock, flags);gfp_temp = gfp_mask;goto repeat_alloc;//跳到repeat_alloc重新获取一次}/* We must not sleep if !__GFP_DIRECT_RECLAIM *///传入的参数gfp_mask不允许回收的等待,分配不到内存则直接退出if (!(gfp_mask & __GFP_DIRECT_RECLAIM)) {spin_unlock_irqrestore(&pool->lock, flags);return NULL;}/* Let's wait for someone else to return an element to @pool */init_wait(&wait);//初始化wait等待进程//加入到内存池的等待队列中,等待当内存池中有空闲对象或者等待超时prepare_to_wait(&pool->wait, &wait, TASK_UNINTERRUPTIBLE);spin_unlock_irqrestore(&pool->lock, flags);/* * FIXME: this should be io_schedule(). The timeout is there as a * workaround for some DM problems in 2.6.18. */io_schedule_timeout(5*HZ);//阻塞等待5秒finish_wait(&pool->wait, &wait);//从内存池的等待队列删除此进程goto repeat_alloc;//跳转到repeat_alloc,重新尝试获取内存对象}EXPORT_SYMBOL(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缓冲区中。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。