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_fn
static 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_list
static __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_list
static 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
来删除这个页面.
lru的加入与删除
最新推荐文章于 2024-08-16 15:19:46 发布