LRU页面回收

内存回收算法总是会在一定的时间将一些内存回收, 内存回收算法是通过LRU链表对page页面进行管理的,对于那些新的页面会将其插入到LRU链表头,回收时将返回LRU链表末尾的元素,代表老化程度最高的页面

基本数据结构

typedef struct pglist_data {
...
       /* Fields commonly accessed by the page reclaim scanner */                                                                                              
        
        /*
         * NOTE: THIS IS UNUSED IF MEMCG IS ENABLED.                                                                                                            
         *
         * Use mem_cgroup_lruvec() to look up lruvecs.                                                                                                          
         */
        struct lruvec           __lruvec;
...

内存页回收的管理是以node为单位的,pglist_data 中会包含一个struct lruvec数组

struct lruvec {
        struct list_head                lists[NR_LRU_LISTS];
...

这个struct lruvec数组包含了NR_LRU_LISTS个LRU链表元素,这些链表元素定义如下,每个链表将放置不同类别的page,相关类别由enum lru_list定义

enum lru_list {
        LRU_INACTIVE_ANON = LRU_BASE,
        LRU_ACTIVE_ANON = LRU_BASE + LRU_ACTIVE,
        LRU_INACTIVE_FILE = LRU_BASE + LRU_FILE,
        LRU_ACTIVE_FILE = LRU_BASE + LRU_FILE + LRU_ACTIVE,
        LRU_UNEVICTABLE,
        NR_LRU_LISTS
};
//mm/swap.c
/*                                                                                                                                                              
 * The following struct pagevec are grouped together because they are protected                                                                                 
 * by disabling preemption (and interrupts remain enabled).                                                                                                     
 */                                                                                                                                                             
struct lru_pvecs {                                                                                                                                              
        local_lock_t lock;                                                                                                                                      
        struct pagevec lru_add;                                                                                                                                 
        struct pagevec lru_deactivate_file;                                                                                                                     
        struct pagevec lru_deactivate;                                                                                                                          
        struct pagevec lru_lazyfree;                                                                                                                            
#ifdef CONFIG_SMP                                                                                                                                               
        struct pagevec activate_page;                                                                                                                           
#endif                                                                                                                                                          
};                                                                                                                                                              
static DEFINE_PER_CPU(struct lru_pvecs, lru_pvecs) = {                                                                                                          
        .lock = INIT_LOCAL_LOCK(lock),                                                                                                                          
};

内核同时定义了struct lru_pvecs , 它包含了5个struct pagevec变量

struct pagevec {                                                                                                                                                
        unsigned char nr;                                                                                                                                       
        bool percpu_pvec_drained;                                                                                                                               
        struct page *pages[PAGEVEC_SIZE];                                                                                                                       
};

这些struct pagevec变量实际是用来管理具体的page页面的
如上相关结构体有如下的对应关系
在这里插入图片描述

lru_cache_add

/**
 * lru_cache_add - add a page to a page list
 * @page: the page to be added to the LRU.
 *
 * Queue the page for addition to the LRU via pagevec. The decision on whether
 * to add the page to the [in]active [file|anon] list is deferred until the
 * pagevec is drained. This gives a chance for the caller of lru_cache_add()
 * have the page added to the active list using mark_page_accessed().
 */
void lru_cache_add(struct page *page)
{
        struct pagevec *pvec;

        VM_BUG_ON_PAGE(PageActive(page) && PageUnevictable(page), page);
        VM_BUG_ON_PAGE(PageLRU(page), page);

        get_page(page);
        local_lock(&lru_pvecs.lock);
        pvec = this_cpu_ptr(&lru_pvecs.lru_add);
        if (!pagevec_add(pvec, page) || PageCompound(page))
                __pagevec_lru_add(pvec);
        local_unlock(&lru_pvecs.lock);
}
EXPORT_SYMBOL(lru_cache_add);
  1. pagevec_add:实际就是将页面加入到struct lru_pvecs的某个struct pagevec中,这里实际是加入到lru_pvecs.lru_add中,并返回当前struct pagevec可管理的剩余的page个数
/*                                                                                                                                                              
 * Add a page to a pagevec.  Returns the number of slots still available.                                                                                       
 */                                                                                                                                                             
static inline unsigned pagevec_add(struct pagevec *pvec, struct page *page)                                                                                     
{                                                                                                                                                               
        pvec->pages[pvec->nr++] = page;                                                                                                                         
        return pagevec_space(pvec);                                                                                                                             
}
  1. __pagevec_lru_add:如果struct pagevec可管理的剩余的page个数为0,则需要执行__pagevec_lru_add,它实际是将已经满的struct pagevec中的page页迁移到某个lru链表中去,至于迁移到哪个lru链表,由当前page所处的struct pagevec类别 以及page自身的flags变量决定,迁移时会考虑到各个页面的老化程度,越老的页面越会靠近lru链表的后面,经典内存回收算法将扫描lru链表选取相应的页面回收

lru_to_page

#define lru_to_page(head) (list_entry((head)->prev, struct page, lru))

将返回链表的最后一个元素,它代表老化程度最高的一个page

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值