本文共 2052 字,大约阅读时间需要 6 分钟。
LRU的全称是least recently used的缩写,在kernel中当内存紧张是总是有限换出page cache的页面针对LRU换出页面的的算法旧版采用的是FIFO算法,新版采用的是second chance算法。将page加入到lru中的flow如下:lru_cache_add->__lru_cache_add->__pagevec_lru_add->pagevec_lru_move_fn->void __pagevec_lru_add(struct pagevec *pvec){ pagevec_lru_move_fn(pvec, __pagevec_lru_add_fn, NULL);}可见这里的回调函数是__pagevec_lru_add_fnstatic void pagevec_lru_move_fn(struct pagevec *pvec, void (*move_fn)(struct page *page, struct lruvec *lruvec, void *arg), void *arg){ int i; struct pglist_data *pgdat = NULL; struct lruvec *lruvec; unsigned long flags = 0; for (i = 0; i < pagevec_count(pvec); i++) { struct page *page = pvec->pages[i]; struct pglist_data *pagepgdat = page_pgdat(page); if (pagepgdat != pgdat) { if (pgdat) spin_unlock_irqrestore(&pgdat->lru_lock, flags); pgdat = pagepgdat; spin_lock_irqsave(&pgdat->lru_lock, flags); } #注意这里的变量lruvec 是每个node节点都有一个。 lruvec = mem_cgroup_page_lruvec(page, pgdat); (*move_fn)(page, lruvec, arg); }}加入我们没有定义cgroup的话static inline struct lruvec *mem_cgroup_page_lruvec(struct page *page, struct pglist_data *pgdat){ return &pgdat->lruvec;}从这个函数很容易看出lruvec是没有个node 节点都有一个。从这里也可以知道lru的管理是一个numa node每最大支持范围。__pagevec_lru_add_fn->add_page_to_lru_liststatic __always_inline void add_page_to_lru_list(struct page *page, struct lruvec *lruvec, enum lru_list lru){ update_lru_size(lruvec, lru, page_zonenum(page), hpage_nr_pages(page)); list_add(&page->lru, &lruvec->lists[lru]);}可见最后通过update_lru_size 来更新lru的size,然后在通过list_add 将page->lru 添加到lruvec->lists[lru] 中当内存不足时会调用reclaim_clean_pages_from_list或者shrink_inactive_list 来回收lru中的页面,这两个函数最终都会调用shrink_page_liststatic unsigned long shrink_page_list(struct list_head *page_list, struct pglist_data *pgdat, struct scan_control *sc, enum ttu_flags ttu_flags, struct reclaim_stat *stat, bool force_reclaim){#调用下面这两个函数来从lru的list中删除page page = lru_to_page(page_list); list_del(&page->lru);}其中#define lru_to_page(head) (list_entry((head)->prev, struct page, lru)) 可以得到一个页面,然后调用list_del来删除这个页面.
转载地址:http://jnnmi.baihongyu.com/