关于Kswapd的理解(一)
关于Kswapd的理解(一)
之前跟踪了MemAvailable的计算方式,其中watermark的计算花了很多精力,在学习的过程中看到一些资料中说watermark是给swapd使用的,于是乎,研究一下看看咯
本部分主要记录两个方面:
- 对于kswapd这个东西,从初学者(我)的角度出发会考虑哪些内容?
- 跟踪kswapd 初始化结构部分;
1. KSwapd机制
上图是还没有跟踪kswapd code的时候,对自己提出的一些疑问和自身的理解,主要是如下内容:
- kswapd 机制用来做什么:内存不足时回收内存;
- kswapd 机制的载体是什么:启动一个内核线程用于回收mem,平时进入sleep状态;
- kswapd 线程满足什么条件会被唤醒:当前来看分配mem时低于watermark[high] 的时候会被唤醒;
- kswapd 机制会回收哪些内存:anon & file ,即我们俗称的cache;
- kswapd 机制如何回收:从根本上讲,主要是discard、swap到emmc、zram等几种方式;
- kswapd 机制回收内存的具体策略是:还在学习中,后续持续补充;
2. code 跟踪
接下来具体来跟踪code来理解kswapd
2.1 kswapd 初始化
初始化部分的调用逻辑非常简单:
这部分平平无奇:主要逻辑就是kthread_run 起一个线程运行kswapd这个功能函数;
//task 初始化部分,这种task很明确的,在init的时候run起来,在需要的时候唤醒;
static int __init kswapd_init(void)
{int nid;swap_setup();for_each_node_state(nid, N_MEMORY) //每个node run一个taskkswapd_run(nid);hotcpu_notifier(cpu_callback, 0);return 0;
}int kswapd_run(int nid)
{pg_data_t *pgdat = NODE_DATA(nid);int ret = 0;if (pgdat->kswapd)return 0;pgdat->kswapd = kthread_run(kswapd, pgdat, "kswapd%d", nid);//起一个线程执行kswapdif (IS_ERR(pgdat->kswapd)) {//失败后处理,不重要BUG_ON(system_state == SYSTEM_BOOTING);pr_err("Failed to start kswapd on node %d\n", nid);ret = PTR_ERR(pgdat->kswapd);pgdat->kswapd = NULL;}return ret;
}
2.2 swapd 功能函数
这里是本小节中最核心的部分了:
跟常规理解的一样:
- 主要逻辑是一个for循环,检测到满足条件(zone_balanced)就让出CPU进入睡眠等待;
- 被唤醒后根据条件不同,主要会进行balance_pgdat操作进行内存回收;
具体来看code(注释):
static int kswapd(void *p)
{unsigned int alloc_order, reclaim_order, classzone_idx;pg_data_t *pgdat = (pg_data_t*)p; //当前node指针struct task_struct *tsk = current;//获取到current_taskstruct reclaim_state reclaim_state = {.reclaimed_slab = 0,};const struct cpumask *cpumask = cpumask_of_node(pgdat->node_id);lockdep_set_current_reclaim_state(GFP_KERNEL);if (!cpumask_empty(cpumask))set_cpus_allowed_ptr(tsk, cpumask);current->reclaim_state = &reclaim_state;tsk->flags |= PF_MEMALLOC | PF_SWAPWRITE | PF_KSWAPD;//给task设置标志位;set_freezable();pgdat->kswapd_order = alloc_order = reclaim_order = 0;//申请的order,即2^order这么多pagespgdat->kswapd_classzone_idx = classzone_idx = 0;//处理的zone的标号for ( ; ; ) {//进入循环bool ret;kswapd_try_sleep:kswapd_try_to_sleep(pgdat, alloc_order, reclaim_order, classzone_idx);//核心处理1,实质的就是判断各个zone是否为balanced,是否balanced即判断zone内可申请的mem数量是否在watermark[high] 之上;alloc_order = reclaim_order = pgdat->kswapd_order; //赋值操作classzone_idx = pgdat->kswapd_classzone_idx; pgdat->kswapd_order = 0;pgdat->kswapd_classzone_idx = 0;ret = try_to_freeze();//判断下当前是否是休眠操作if (kthread_should_stop())//是否有人调用thread_stop,正常情况下就是在module_exit时候调用;break;if (ret)//如果是suspend状态的话,就啥也不干,继续循环continue;trace_mm_vmscan_kswapd_wake(pgdat->node_id, classzone_idx, alloc_order);//trace 埋点,对于理解流程不需要关注;reclaim_order = balance_pgdat(pgdat, alloc_order, classzone_idx);//核心处理2,进行实质回收操作if (reclaim_order < alloc_order)//回收数量不够,则再来一次;goto kswapd_try_sleep;alloc_order = reclaim_order = pgdat->kswapd_order;classzone_idx = pgdat->kswapd_classzone_idx;}tsk->flags &= ~(PF_MEMALLOC | PF_SWAPWRITE | PF_KSWAPD);current->reclaim_state = NULL;lockdep_clear_current_reclaim_state();return 0;
}
这个主循环实际上干了两件事:
- 判断当前是否可以sleep,如果可以就让出了CPU(kswapd_try_to_sleep);
- 被唤醒后调用balance_pgdat 进行mem 回收操作;
2.2.1 kswapd_try_to_sleep
本接口即判断各个zone是否都balanced,如果都满足,就进入sleep让出CPU:
直接上code:
static void kswapd_try_to_sleep(pg_data_t *pgdat, int alloc_order, int reclaim_order,unsigned int classzone_idx)
{long remaining = 0;DEFINE_WAIT(wait);//当前taskif (freezing(current) || kthread_should_stop())//如果需要退出,则直接返回return;prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE);// wait加入kswap_wait queue中,即等待被唤醒,注意此时没有让出CPUif (prepare_kswapd_sleep(pgdat, reclaim_order, classzone_idx)) {//核心处理1:判断是否各个zone都是balancedreset_isolation_suitable(pgdat);wakeup_kcompactd(pgdat, alloc_order, classzone_idx);//唤醒compact线程处理,这个是压缩内存的一个机制,这里不做详细讨论;remaining = schedule_timeout(HZ/10); //sleep 100msif (remaining) {//remaining > 0说明被唤醒而非100ms结束pgdat->kswapd_classzone_idx = max(pgdat->kswapd_classzone_idx, classzone_idx);pgdat->kswapd_order = max(pgdat->kswapd_order, reclaim_order);}finish_wait(&pgdat->kswapd_wait, &wait);//将wait从kswapd_wait queue中移除,并将当前状态配置为runningprepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE);//将wait加入kswapd wait queue,这里看起来是确保queue中只有一个等待事件;}if (!remaining &&//上述没有被唤醒prepare_kswapd_sleep(pgdat, reclaim_order, classzone_idx)) {//各个zone都是balancedtrace_mm_vmscan_kswapd_sleep(pgdat->node_id);set_pgdat_percpu_threshold(pgdat, calculate_normal_threshold);if (!kthread_should_stop())//没有需要退出thread,则真正的进入睡眠,主动调用schedule调度下一个事件;schedule();set_pgdat_percpu_threshold(pgdat, calculate_pressure_threshold);} else {if (remaining)//之前是被唤醒的,则记录vmcount_vm_event(KSWAPD_LOW_WMARK_HIT_QUICKLY);elsecount_vm_event(KSWAPD_HIGH_WMARK_HIT_QUICKLY);}finish_wait(&pgdat->kswapd_wait, &wait);//将wait从kswapd_wait queue中移除,并将当前状态配置为running,额 睡醒了
}
这里充分的演示了如何进入睡眠的整个操作:
- prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE)
- if (!kthread_should_stop()) schedule()
- finish_wait(&pgdat->kswapd_wait, &wait)
2.2.2 prepare_kswapd_sleep
本接口功能即判断是否各个zone都属于balanced,如果属于则可以sleep,意味着做好了入睡的准备
static bool prepare_kswapd_sleep(pg_data_t *pgdat, int order, int classzone_idx)
{int i;if (waitqueue_active(&pgdat->pfmemalloc_wait))//判断performance中有无等待task,有的话唤醒wake_up_all(&pgdat->pfmemalloc_wait);for (i = 0; i <= classzone_idx; i++) {//遍历每个zonestruct zone *zone = pgdat->node_zones + i;if (!managed_zone(zone))continue;if (!zone_balanced(zone, order, classzone_idx))//是否balancedreturn false;}return true;
}
//第一层遍历每一个zone,判断是否balanced
static bool zone_balanced(struct zone *zone, int order, int classzone_idx)
{unsigned long mark = high_wmark_pages(zone);//注意这个mark是watermark[high]if (!zone_watermark_ok_safe(zone, order, mark, classzone_idx))//判断是否ok,这里就是判断是否高于watermark[high]return false;clear_bit(PGDAT_CONGESTED, &zone->zone_pgdat->flags);clear_bit(PGDAT_DIRTY, &zone->zone_pgdat->flags);return true;
}
//第二层获取到了mark的值,又套了一层
bool zone_watermark_ok_safe(struct zone *z, unsigned int order,unsigned long mark, int classzone_idx)
{long free_pages = zone_page_state(z, NR_FREE_PAGES);if (z->percpu_drift_mark && free_pages < z->percpu_drift_mark)free_pages = zone_page_state_snapshot(z, NR_FREE_PAGES);//获取freepage数量return __zone_watermark_ok(z, order, mark, classzone_idx, 0, free_pages);//又套了一层,终于进来了
}
//第三层获取到freepage的值,又套了一层bool __zone_watermark_ok(struct zone *z, unsigned int order, unsigned long mark,int classzone_idx, unsigned int alloc_flags,long free_pages)
{long min = mark;int o;const bool alloc_harder = (alloc_flags & ALLOC_HARDER);//我们是否需要更harder的申请内存呢?free_pages -= (1 << order) - 1;//去除申请order的pagesif (alloc_flags & ALLOC_HIGH)//对于highmem,不需要保留那么多,去除一半;min -= min / 2;if (likely(!alloc_harder))//我们还没到那么艰难,则在free中去除rserved数量free_pages -= z->nr_reserved_highatomic;else //假如我们已经很艰难了,那么保留的值再减少1/4吧,这里跟计算high的过程挺像;min -= min / 4;#ifdef CONFIG_CMA//CMA部分先不careif (!(alloc_flags & ALLOC_CMA))free_pages -= zone_page_state(z, NR_FREE_CMA_PAGES);
#endifif (free_pages <= min + z->lowmem_reserve[classzone_idx])//free比起min + reserve要小,即不balancereturn false;if (!order)//order为0,即申请一个page,而且上边一个判断过去了,说明可以申请到,即balancedreturn true;for (o = order; o < MAX_ORDER; o++) {struct free_area *area = &z->free_area[o];int mt;if (!area->nr_free)continue;if (alloc_harder)return true;for (mt = 0; mt < MIGRATE_PCPTYPES; mt++) {if (!list_empty(&area->free_list[mt]))return true;}#ifdef CONFIG_CMAif ((alloc_flags & ALLOC_CMA) &&!list_empty(&area->free_list[MIGRATE_CMA])) {return true;}
#endif}return false;
}
//第四层终于看到了实际的判断逻辑,其实就是watermark + reserve这些,另外需要考虑迁移策略;
本部分用一句话描述就是,判断各个zone的内存是否够用;
2.3 kswapd回收
这里是kswapd被唤醒后的处理,被唤醒的话,那就要进行kswapd实际回收操作了:
static int balance_pgdat(pg_data_t *pgdat, int order, int classzone_idx)
{int i;unsigned long nr_soft_reclaimed;unsigned long nr_soft_scanned;struct zone *zone;struct scan_control sc = {.gfp_mask = GFP_KERNEL,.order = order,.priority = DEF_PRIORITY,.may_writepage = !laptop_mode,.may_unmap = 1,.may_swap = 1,};count_vm_event(PAGEOUTRUN);do {bool raise_priority = true; //默认将raise_priority配置为truesc.nr_reclaimed = 0;sc.reclaim_idx = classzone_idx;if (buffer_heads_over_limit) {//这里是判断buffer_head的limit,如果是buffer_head内存较多的话优先从highmem进行,这里标记了进行回收的zonefor (i = MAX_NR_ZONES - 1; i >= 0; i--) {//按照high -- normal -- dma的顺序进行zone = pgdat->node_zones + i;if (!managed_zone(zone))//如果没有managed mem则直接进行下一个zonecontinue;sc.reclaim_idx = i;break;}}for (i = classzone_idx; i >= 0; i--) {//zone = pgdat->node_zones + i;if (!managed_zone(zone))continue;if (zone_balanced(zone, sc.order, classzone_idx))//如果该zone满足分配order后仍属于balanced的情况,则说明分配OK,直接跳出;goto out;}age_active_anon(pgdat, &sc);//走到这里说明将各个zone都判断过之后,回收内存仍不够用,所以对anon 进行老化处理;if (sc.priority < DEF_PRIORITY - 2 || !pgdat_reclaimable(pgdat))sc.may_writepage = 1;//优先级高于10还没有搞到足够内存的时候,需要打开writepage//pgdat_reclaimable 这里是做个判断,当前scan的pages数量是否足够多,如果够多还没有out出去的话,加码/* Call soft limit reclaim before calling shrink_node. */sc.nr_scanned = 0;nr_soft_scanned = 0;nr_soft_reclaimed = mem_cgroup_soft_limit_reclaim(pgdat, sc.order, sc.gfp_mask, &nr_soft_scanned);//先进行一次 mem_cgroup的回收sc.nr_reclaimed += nr_soft_reclaimed;if (kswapd_shrink_node(pgdat, &sc))//关键部分,进行shrink_node回收raise_priority = false;if (waitqueue_active(&pgdat->pfmemalloc_wait) && pfmemalloc_watermark_ok(pgdat))//唤醒performance malloc申请wake_up_all(&pgdat->pfmemalloc_wait);if (try_to_freeze() || kthread_should_stop())//suspend或者退出的话,这里直接跳出去;break;if (raise_priority || !sc.nr_reclaimed)//没回收够,则priority--sc.priority--;} while (sc.priority >= 1);out:return sc.order;//返回回收的order数量
}
暂时只能这样了,搞这个的时候突然来了个紧急的活儿
关于Kswapd的理解(一)相关推荐
- 理解Linux的性能
项目中常遇到需要对目前运行的系统进行效率分析,或碰到客户咨询如何优化系统的效率问题.更多的情况是,在系统出现问题的时候,需要分析原因,定位系统故障或瓶颈,当然,最好是可以一并解决故障.但实际上,操作系 ...
- [转]页面回收的理解
页面回收的理解 页面回收的基本概念 本文主要介绍了一些page reclaim机制中的基本概念.这份文档其实也可以看成阅读ULK第17章第一小节的一个读书笔记.虽然ULK已经读了很多遍,不过每一遍还是 ...
- linux内存回收(一)---kswapd回收
正式开始十一之旅,有大量的时间将目前工作中遇到的内存回收进行总结下,主要是对内存回收的整个过程进行重新梳理.在linux操作系统中,当内存充足的时候,内核会尽量使用内存作为文件缓存(page ca ...
- 深入理解Linux内核第3版--笔记-1.pdf
深入理解Linux内核第3版.pdf Understanding the Linux Kernel, 3rd Edition Preface The Audience for T ...
- 深入理解Linux虚拟内存管理(二)
系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核(一) 深入理解 Linux 内核(二) Linux 设备驱动程序(一) Linux 设备驱动程序(二) Linux 设备驱动程序( ...
- 深入理解Linux虚拟内存管理(一)
系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核(一) 深入理解 Linux 内核(二) Linux 设备驱动程序(一) Linux 设备驱动程序(二) Linux 设备驱动程序( ...
- 深入理解Linux内核之内核线程(上)
1.开场白 环境: 处理器架构:arm64 内核源码:linux-5.11 ubuntu版本:20.04.1 代码阅读工具:vim+ctags+cscope 在linux系统中, 我们接触最多的莫过于 ...
- 深入理解Linux内存管理
1.1 内存管理的意义 1.2 原始内存管理 1.3 分段内存管理 1.4 分页内存管理 1.5 内存管理的目标 1.6 Linux内存管理体系 2.1 物理内存节点 2.2 物理内存区域 2.3 物 ...
- 深入理解Linux内存管理(0.3)
学习方法论 写作原则 标题括号中的数字代表完成度与完善度 0.0-1.0 代表完成度,1.1-1.5 代表完善度 0.0 :还没开始写 0.1 :写了一个简介 0.3 :写了一小部分内容 0.5 :写 ...
- swap、swappiness以及kswapd
前言 作为一个性能优化学习者,内存相关的知识是一定要了解的.那么为了咱们提升自己便于日后娶走嘉然 (不是),首先要在内心有概念的就是swap.swappiness以及kswapd. SWAP是什么 从 ...
最新文章
- Nat. Biotechnol.| 基于生物活性建模识别抗SARS-CoV-2药物
- 【262】pscp命令 实现windows与linux互传文件
- page rank算法
- css之px自动转rem—sublime 插件CSSREM
- 判断给定的二叉树是否为二叉排序树
- 数据连接之--Datalist 的使用(查看、编辑、删除)
- Docker跨服务器通信Overlay解决方案(上) Consul单实例
- java基础算法题(入门题与简单题)
- JS实现文字向上无缝滚动轮播
- pytnon 学习day-1
- 02.规划过程组表格-沟通管理计划
- 总有被遗忘或者没有及时跟进的工作
- Line3d安装;bundler+pmvs;opencv_contrib
- excel对比两列不同
- 华为手机备份的通讯录是什么文件_华为手机怎样备份手机通讯录(教你微信如何备份手机通讯...
- Pwn-2018_HITB_CTF-gundam
- 【第三方互联】15、百度(baidu)授权第三方登录
- Android开发之手机震动器
- winform datagridview 没有出现垂直滚动条 上下_木门安装中出现问题如何解决?
- 黄河小浪底调水调沙问题(mathmatica)