关于Kswapd的理解(一)

之前跟踪了MemAvailable的计算方式,其中watermark的计算花了很多精力,在学习的过程中看到一些资料中说watermark是给swapd使用的,于是乎,研究一下看看咯

本部分主要记录两个方面:

  1. 对于kswapd这个东西,从初学者(我)的角度出发会考虑哪些内容?
  2. 跟踪kswapd 初始化结构部分;

1. KSwapd机制

上图是还没有跟踪kswapd code的时候,对自己提出的一些疑问和自身的理解,主要是如下内容:

  1. kswapd 机制用来做什么:内存不足时回收内存;
  2. kswapd 机制的载体是什么:启动一个内核线程用于回收mem,平时进入sleep状态;
  3. kswapd 线程满足什么条件会被唤醒:当前来看分配mem时低于watermark[high] 的时候会被唤醒;
  4. kswapd 机制会回收哪些内存:anon & file ,即我们俗称的cache;
  5. kswapd 机制如何回收:从根本上讲,主要是discard、swap到emmc、zram等几种方式;
  6. 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 功能函数

这里是本小节中最核心的部分了:

跟常规理解的一样:

  1. 主要逻辑是一个for循环,检测到满足条件(zone_balanced)就让出CPU进入睡眠等待;
  2. 被唤醒后根据条件不同,主要会进行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;
}

这个主循环实际上干了两件事:

  1. 判断当前是否可以sleep,如果可以就让出了CPU(kswapd_try_to_sleep);
  2. 被唤醒后调用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,额 睡醒了
}

这里充分的演示了如何进入睡眠的整个操作:

  1. prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE)
  2. if (!kthread_should_stop()) schedule()
  3. 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的理解(一)相关推荐

  1. 理解Linux的性能

    项目中常遇到需要对目前运行的系统进行效率分析,或碰到客户咨询如何优化系统的效率问题.更多的情况是,在系统出现问题的时候,需要分析原因,定位系统故障或瓶颈,当然,最好是可以一并解决故障.但实际上,操作系 ...

  2. [转]页面回收的理解

    页面回收的理解 页面回收的基本概念 本文主要介绍了一些page reclaim机制中的基本概念.这份文档其实也可以看成阅读ULK第17章第一小节的一个读书笔记.虽然ULK已经读了很多遍,不过每一遍还是 ...

  3. linux内存回收(一)---kswapd回收

    ​ 正式开始十一之旅,有大量的时间将目前工作中遇到的内存回收进行总结下,主要是对内存回收的整个过程进行重新梳理.在linux操作系统中,当内存充足的时候,内核会尽量使用内存作为文件缓存(page ca ...

  4. 深入理解Linux内核第3版--笔记-1.pdf

    深入理解Linux内核第3版.pdf         Understanding the Linux Kernel, 3rd Edition Preface    The Audience for T ...

  5. 深入理解Linux虚拟内存管理(二)

    系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核(一) 深入理解 Linux 内核(二) Linux 设备驱动程序(一) Linux 设备驱动程序(二) Linux 设备驱动程序( ...

  6. 深入理解Linux虚拟内存管理(一)

    系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核(一) 深入理解 Linux 内核(二) Linux 设备驱动程序(一) Linux 设备驱动程序(二) Linux 设备驱动程序( ...

  7. 深入理解Linux内核之内核线程(上)

    1.开场白 环境: 处理器架构:arm64 内核源码:linux-5.11 ubuntu版本:20.04.1 代码阅读工具:vim+ctags+cscope 在linux系统中, 我们接触最多的莫过于 ...

  8. 深入理解Linux内存管理

    1.1 内存管理的意义 1.2 原始内存管理 1.3 分段内存管理 1.4 分页内存管理 1.5 内存管理的目标 1.6 Linux内存管理体系 2.1 物理内存节点 2.2 物理内存区域 2.3 物 ...

  9. 深入理解Linux内存管理(0.3)

    学习方法论 写作原则 标题括号中的数字代表完成度与完善度 0.0-1.0 代表完成度,1.1-1.5 代表完善度 0.0 :还没开始写 0.1 :写了一个简介 0.3 :写了一小部分内容 0.5 :写 ...

  10. swap、swappiness以及kswapd

    前言 作为一个性能优化学习者,内存相关的知识是一定要了解的.那么为了咱们提升自己便于日后娶走嘉然 (不是),首先要在内心有概念的就是swap.swappiness以及kswapd. SWAP是什么 从 ...

最新文章

  1. Nat. Biotechnol.| 基于生物活性建模识别抗SARS-CoV-2药物
  2. 【262】pscp命令 实现windows与linux互传文件
  3. page rank算法
  4. css之px自动转rem—sublime 插件CSSREM
  5. 判断给定的二叉树是否为二叉排序树
  6. 数据连接之--Datalist 的使用(查看、编辑、删除)
  7. Docker跨服务器通信Overlay解决方案(上) Consul单实例
  8. java基础算法题(入门题与简单题)
  9. JS实现文字向上无缝滚动轮播
  10. pytnon 学习day-1
  11. 02.规划过程组表格-沟通管理计划
  12. 总有被遗忘或者没有及时跟进的工作
  13. Line3d安装;bundler+pmvs;opencv_contrib
  14. excel对比两列不同
  15. 华为手机备份的通讯录是什么文件_华为手机怎样备份手机通讯录(教你微信如何备份手机通讯...
  16. Pwn-2018_HITB_CTF-gundam
  17. 【第三方互联】15、百度(baidu)授权第三方登录
  18. Android开发之手机震动器
  19. winform datagridview 没有出现垂直滚动条 上下_木门安装中出现问题如何解决?
  20. 黄河小浪底调水调沙问题(mathmatica)

热门文章

  1. c#Winform程序CPU占用高的原因和解决方法(转载)
  2. [CTSC2018]假面(概率DP)
  3. 模拟百度搜索框,输入时显示历史记录
  4. UVa 10499 - The Land of Justice
  5. 32bit程序在64bit操作系统下处理重定向细节
  6. JDK和CGLIB生成动态代理类的区别
  7. Go 性能优化技巧 4/10
  8. Oracle Flash Storage System新版手册集
  9. .NET URL 301转向方法的实现
  10. Handler+MessageQueue等操作