在kernel中会为每个numa节点建一个内核线程kswapd,主要用于在内存不足时回收页面
static int __init kswapd_init(void)
{
int nid, ret;
swap_setup();
#遍历所有的numa节点,每个numa节点调用kswapd_run
for_each_node_state(nid, N_MEMORY)
kswapd_run(nid);
ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
"mm/vmscan:online", kswapd_cpu_online,
NULL);
WARN_ON(ret < 0);
return 0;
}
#开机自动运行
module_init(kswapd_init)
kswapd_run的实现如下:
int kswapd_run(int nid)
{
pg_data_t *pgdat = NODE_DATA(nid);
int ret = 0;
#如果这个numa节点已经有回收内存的线程,则退出。这里看到每个numa
#节点只能由一个线程用来回收页面
if (pgdat->kswapd)
return 0;
#通过kernel_run 建立一个内核线程,并立刻开始运行,这个内核线程的name是kswapd + 节点id
pgdat->kswapd = kthread_run(kswapd, pgdat, "kswapd%d", nid);
if (IS_ERR(pgdat->kswapd)) {
/* failure at boot is fatal */
BUG_ON(system_state < SYSTEM_RUNNING);
pr_err("Failed to start kswapd on node %d\n", nid);
ret = PTR_ERR(pgdat->kswapd);
pgdat->kswapd = NULL;
}
return ret;
}
static int kswapd(void *p)
{
for ( ; ; ) {
bool ret;
kswapd_try_sleep:
#可见这个内核线程一般情况下是在sleep
kswapd_try_to_sleep(pgdat, alloc_order, reclaim_order,
classzone_idx);
fs_reclaim_acquire(GFP_KERNEL);
#调用balance_pgdat 开始回收内存页面
reclaim_order = balance_pgdat(pgdat, alloc_order, classzone_idx);
fs_reclaim_release(GFP_KERNEL);
if (reclaim_order < alloc_order)
goto kswapd_try_sleep;
}
}
alloc_pages_node->__alloc_pages->__alloc_pages_nodemask->__alloc_pages_slowpath
static inline struct page *
__alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
struct alloc_context *ac)
{
#从这里知道只有申请页面中包含__GFP_KSWAPD_RECLAIM时,在内存不足时才会调用wake_all_kswapds 来wakeup线程来回收页面
if (gfp_mask & __GFP_KSWAPD_RECLAIM)
wake_all_kswapds(order, ac);
}
页面回收线程
最新推荐文章于 2023-02-27 09:15:02 发布