linux内核异步内存回收的另一个思路:基于冷热文件的冷热区域精准的回收冷文件页( 五 )


接着来到第2个内存回收周期,全局age加1为1 。test文件的索引是16、20的文件页、被读取了两次,则对应的内存page单元、的age被赋值为全局age,且增加到2 。接着还要把、移动到->链表头,因为被访问过的都要移动到链表头!效果如下图:
注意,这里设定只有的age与全局age的差值大于阀值N(假设N=5)时,才被判定是冷,然后才会回收该对应的文件页page 。显然此时还没有达到内存回收条件的 。
接着来到第3个内存回收周期,全局age加1为2 。这个周期内,该文件的文件页、连续被访问了6次,对应的和的被赋值6 。二者因为访问次数超过了阀值M(假设M=5),则和判定为热 。于是把和移动到->链表,age也被赋值为2 。同时该文件的文件页page5、page8也被访问了一次,则对应的、被移动到->链表头,且age被赋值2,赋值1,最终效果如图所示:
随后一直到周期7,该文件都没再被访问了,此时出现了内存回收契机 。因为此时全局age增加到6,而该文件在->链表尾的内存page单元、对应的文件页page0~page3、~ 一直没有被访问,age是0,与全局age的差值大于阀值N(假设N=5),则二者被判定是冷 。然后在该周期,异步内存回收线程把、移动到->链表 。如图:
注意,的只表示单个周期内被访问的次数 。每个周期开始,的要先清0,然后每被访问一次则加1 。
接着,异步内存回收线程遍历->链表上的和,根据该文件的radix/ tree(保存文件页page指针),找到对应的page0~page3、~文件页指针 。然后就尝试回收掉这8个page,具体回收流程大体模仿内核原生内存回收代码,但有很大改动 。这些文件页page回收完后,把和移动到->链表 。如图:
假设对应的索引是0的文件页page0在内存回收后,索引是0的page立即又被进程访问了 。则发生了现象,于是把移动到-> 链表,这样后期有相当长一段时间不会再被参与内存回收,即便它的age与全局age相差较大 。如图:
好的,以上把针对单个文件的文件页page的内存回收流程大体演示了一遍 。系统有成百上千个文件时,每个文件的文件页page回收过程类似 。但再结合普通文件、大文件、热文件的文件页page的回收,就很复杂了 。下文从代码角度介绍下整体的异步内存回收流程,或许可以加深理解 。
4:基于源码流程图聊聊异步内存回收
该方案的源码主要有两个流程:
1:文件页读写后,会执行到内核原生()函数 。本方案 ()函数(现在也在考虑换成等函数,效果一样,但是可以解决高版本内核 io write不执行函数的问题,阅读本方案的源码时需注意),然后执行自定义()函数 。
在函数()函数里:为读写的文件分配;为访问的文件页page分配对应的;把、添加到各种链表;增加的age和访问计数;判断热;普通文件在达到一定条件升级到大文件或者热文件等等 。
2:异步内存回收线程的入口函数是(),默认一分钟运行一次 。在()函数里:令全局age加1;遍历 大文件链表和 普通文件链表的,从这些文件-> 链表尾找出age与全局age差值大于阀值N的(这些就是冷,对应的文件页page长时间不被访问,本次就回收这些page) 。
如果遍历到的文件的->和-> 链表上的长时间不被访问,则要降级移动到->链表;如果链表上的大文件,对应文件页page减少到阀值以下,则降级为普通文件,并把移动到普通文件 链表;如果 链表上的热文件,大部分不再是热,则也要降级为普通文件,并把移动到普通文件 或大文件 链表等等 。
细节还是比较复杂的,下边先看下文件被读写最后执行到的()函数的整体流程图:
可以发现,这个流程主要就是操作文件和本次访问的文件页page对应的内存page单元:更新 的age、增加计数、热文件和大文件的判定、热、 的判定和处理 。下边看下异步内存回收线程入口函数()的整理流程: