8.17 自适应水位算法分析

2023年 8月 29日 93.7k 0

8.17 自适应水位算法分析

调整时机:

调用 frameworks/base/services/core/java/com/android/server/am/ProcessList.java:updateOomLevels 函数时会进行调整,物理硬件参数更新时会调整,比如分辨率更新。

【影响范围推断】集中于需要进行分辨率调整的feature,多媒体,游戏相关

水位调整定义:

watermark_scale 水位会依据 extra_free_kbytes 去调整

Script implements watermark_scale calculation which results in the same low watermark as if extra_free_kbytes tunable were to be used.

调整目的:

以前仅仅用 extra_free_kbytes 这一个参数去调整了高低水位,于是存在两个考虑不充分的点:

  • 高水位调整不够
  • 没有考虑到 watermark_scale 对高低水位的影响。
  • 现在想要通过 extra_free_pages 去计算迭代出来 watermark_scale,从而同时解决以上两个问题

    Because Android uses extra_free_kbytes to adjust the low watermark, we ignore the difference in how watermark_scale and extra_free_kbytes affect the high watermark and will match the low watermark only.

    调整算法过程如下:

    extra_free_pages = extra_free_kbytes / page_size

    extra_share = extra_free_pages * managed_pages / vm_total_pages

    low = min + max(min / 4, managed_pages * (watermark_scale / 10000)) + extra_share

    high = min + 2 * max(min / 4, managed_pages * (watermark_scale / 10000)) + extra_share

    下面是根据 max 函数去迭代 watermark_scale_new,

    max(min/4,managed_pages⋅(watermark_scale/10000))+extra_share=max(min/4,managed_pages⋅ (watermark_scale_new/10000))−−−−−−①max(min / 4, managed\_pages \cdot (watermark\_scale / 10000)) + extra\_share = max(min / 4, managed\_pages \cdot (watermark\_scale\_new / 10000)) ------ ① max(min/4,managed_pages⋅(watermark_scale/10000))+extra_share=max(min/4,managed_pages⋅ (watermark_scale_new/10000))−−−−−−①

    根据 max 函数要么大,要么小,很容易分成两种计算方式

    A. managed_pages * (watermark_scale / 10000) > min / 4

    则 ① 易得:

    managed_pages * (watermark_scale / 10000) + extra_share = managed_pages * (watermark_scale_new / 10000)

    => watermark_scale_new = watermark_scale + extra_free_pages / vm_total_pages * 10000

    B. managed_pages * (watermark_scale / 10000) < min / 4

    则 ① 易得:

    min / 4 + extra_share = max(min / 4, managed_pages * (watermark_scale_new / 10000))

    等式右边只能取 managed_pages * (watermark_scale_new / 10000)

    => watermark_scale_new = (min / 4 + extra_share) / managed_pages * 10000

    => watermark_scale_new = (min / 4) * 10000 / managed_pages + extra_free_pages / vm_total_pages * 10000

    不妨设:watermark_delta = extra_free_pages / vm_total_pages * 10000

    则不难得出以下迭代逻辑:

    if (managed_pages * (watermark_scale / 10000) > min / 4)
         watermark_scale_new = watermark_scale + watermark_delta
    else
         watermark_scale_new = (min / 4) * 10000 / managed_pages + watermark_delta
    

    适配要求

    以目前现有的 kernel 版本讨论

  • /proc/sys/vm/extra_free_kbytes 节点存在,不会进入水位调整逻辑。kernel 4.19、kernel 5.10、kernel 5.4 存在此节点,kernel 5.15 删除此节点
  • kernel 4.19、kernel 5.10、kernel 5.4 存在 extra_free_kbytes节点,kernel 5.15 删除此节点
    - 存在extra_free_kbytes节点时,在源码 sysctl.c 中会存在此 node 的相关信息,
    - 并且 adb shell 在 /proc/system/vm/extra_free_kbytes 也可以查询到节点信息
    
    /bsp/kernel/kernel5.4/kernel/sysctl.c
    
    extern int extra_free_kbytes;
    .procname = "extra_free_kbytes",
    .data = &extra_free_kbytes,
    .maxlen = sizeof(extra_free_kbytes)
    
  • /system/core/init/extra_free_kbytes.sh 自启脚本
  • 1 从 andriod T 开始 init.rc 中通过属性 sys.sysctl.extra_free_kbytes 触发此开机脚本,而 android S 上此部分是直接写入 /proc/sys/vm/extra_free_kbytes 节点
    
    android T --->
    /system/core/rootdir/init.rc
    on property:sys.sysctl.extra_free_kbytes=*
    exec -- /system/bin/extra_free_kbytes.sh ${sys.sysctl.extra_free_kbytes}
    
    android S --->
    on property:sys.sysctl.extra_free_kbytes=*
    write /proc/sys/vm/extra_free_kbytes ${sys.sysctl.extra_free_kbytes}
    

    extra_free_kbytes 和 watermarker_scale 简述

    extra_free_kbytes 从framework层中会不断调用 updateOomLevels 来更新值,在这个文件中

    frameworks/base/services/core/java/com/android/server/am/ProcessList.java:updateOomLevels

    基于屏幕尺寸以及机器的CPU位數,更新水位线的

    private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
    ...
        int reserve = displayWidth * displayHeight * 4 * 3 / 1024;
        int reserve_adj =    Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAdjust);
        int reserve_abs = Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAbsolute);
    ...
        SystemProperties.set("sys.sysctl.extra_free_kbytes", Integer.toString(reserve));
    }
    

    从下面这张图可以看出:

    • 如果空闲页数目min值,则该zone非常缺页,页面回收压力很大,应用程序写内存操作就会被阻塞,直接在应用程序的进程上下文中进行回收,即direct reclaim。
    • 如果空闲页数目小于low值,kswapd线程将被唤醒,并开始释放回收页面。
    • 如果空闲页面的值大于high值,则该zone的状态很完美, kswapd线程将重新休眠。

    在内存分配时,只有"low"与"min"之间之间的这段区域才是kswapd的活动空间,低于了"min"会触发direct reclaim,高于了"low"又不会唤醒kswapd,而Linux中默认的"low"与"min"之间的差值确实显得小了点。

    「extra_free_kbytes」:

    源于此,安卓在linux水位的基础上增加了extra_free_kbytes的变量,这个extra时额外加在low和min之间的,它在min不变的情况下,让low值有所增大。想要知道extra_free_kbytes的引入是否取得效果,可以通过/proc/vmstat中的pageoutrun和allocstall来看,两者分别代表了kswapd和direct reclaim启动的次数。

    「watermark_scale_factor」:

    内核总是在进步的,在linux内核4.6版本中,又诞生了一种新的调节水位的方式,即watermark_scale_factor系数,其默认值是10,对应内存占比10/10000=0.1%,可通过/proc/sys/vm/watermark_scale_factor设置,最大值是1000。举个例子:当其被设为1000时,意味着min与low之间的差值,low与high之间的差值都将是内存大小的10%(1000/10000)。前面讲的extra_free_kbytes的方式只增大了min和low之间的差值,而watermark_scale_factor则同时增大了min和low,low和high之间的差值。

    补充 extra_free_kbytes.sh 调整逻辑

    相关文章

    服务器端口转发,带你了解服务器端口转发
    服务器开放端口,服务器开放端口的步骤
    产品推荐:7月受欢迎AI容器镜像来了,有Qwen系列大模型镜像
    如何使用 WinGet 下载 Microsoft Store 应用
    百度搜索:蓝易云 – 熟悉ubuntu apt-get命令详解
    百度搜索:蓝易云 – 域名解析成功但ping不通解决方案

    发布评论