首页 > 系统相关 >psi 跟Android内存优化

psi 跟Android内存优化

时间:2024-11-29 18:22:48浏览次数:10  
标签:psi 内存 memory 进程 Android adj event

概述

lowmemorykiller的作用就是当内存比较紧张的时候去及时杀掉一些对用户来说不那么重要的进程,回收内存,保证手机的正常运行。

安卓平台lowmemorykiller机制演进可以描述为:从早期的Kernel space Lowmemorykiller 到 UserSpace Lowmemorykiller (监听vmpressure),再到UserSpace Lowmemorykiller (监听PSI)。

内核空间LMK

Kernel LMK相关概念

  • /sys/module/lowmemorykiller/parameters/minfree:

18432,23040,27648,32256,55296,80640

数字代表一个内存级别

当手机内存低于80640时,就去杀掉优先级906以及以上级别的进程,当内存低于55296时,就去杀掉优先级900以及以上的进程.

  • proc/pid/oom_adj: 代表当前进程的优先级,这个优先级是kernel中的进程优先级,只读文件。
  • /proc/pid/oom_score_adj: 上层优先级,跟ProcessList中的优先级对应,就是让用户空间调节oom_score的接口。(就是上面的odj值)

AMS: 安卓系统框架层的ActivityManagerService

Lmkd: 安卓用户态的userspace lowmemory killer daemon

Lowmemorykiller 驱动程序允许用户空间指定一组内存阈值,当内存使用达到这些阈值时,具有不同 oom_score_adj 值的进程将被终止。

实践应用举例:

有时在解Android系统连续启动app性能问题时,就要调整上面的lmk水线,即minfree和adj值。因为Android系统现在随着多媒体这些耗内存资源的功能日益强大,

所以为了在有限的内存世界里面,满足app启动性能,就不得不这样调整lmk水线,来灵活干掉后台无挂紧要进程,来释放内存。

AMS与lmkd通过socket通信

主要分为三种command,每种command代表一种数据控制方式:

  • LMK_TARGET:更新/sys/module/lowmemorykiller/parameters/中的minfree以及adj
  • LMK_PROCPRIO:更新指定进程的优先级,也就是oom_score_adj
  • LMK_PROCREMOVE:移除进程。

Lowmemorykiller init

初始化lowmemorykiller,注册shrinker,当空闲内存页面不足时, 内核进程kswpd会调用注册的shrink回调函数来回收页面。

回收内存流程时会被调用,lowmem_scan挂到了register_shrinker里,shrink_slab_node里会scan_objects

调用栈:

• [<c082a824>] (lowmem_scan) from [<c01e0ba4>]
(shrink_slab_node+0x204/0x3d0)
• [<c01e0ba4>] (shrink_slab_node) from [<c01e12b8>]
(shrink_slab+0x70/0xe4)
• [<c01e12b8>] (shrink_slab) from [<c01e3ba8>]
(try_to_free_pages+0x3c0/0x74c)
• [<c01e3ba8>] (try_to_free_pages) from [<c01d9188>]
(__alloc_pages_nodemask+0x578/0x92c)

lowmem_scan首先看看能不能找到oom_score_adj,如果找不到就认为内存充足不杀进程

  • 判断内存是否充足的条件就是other_free和other_file两个都必须同时小于lowmem_minfree中的用户设定值,other_free基本上是free pages,other_file基本上是file pages,两者可以分别看成MemFree和Cached大小
  • 遍历所有的进程for_each_process,查找 oom_score_adj要比min_score_adj大且rss最大的进程,通过send_sig(SIGKILL, selected, 0)杀掉。

Issues with lowmemorykiller kernel driver

1:Hooks into slab shrinker API that was not designed for this purpose. Shrinkers are supposed to quickly drop unused caches and exit in order to avoid slowing down the vmscan process.

Workload that lowmemorykiller performs includes searching for heavy processes and killing them, which are not quick operations。

2:还有个重要的缺陷: 内核是做机制工作的,再实现一些策略工作会比较臃肿。所以尽量剥离一些本来属于策略上的东西(比如上面的依据进程rss大小来选择杀哪些进程),分给Android 上层代码去做。

因为上层代码离业务层很近,更能根据业务需求,来做适合Android场景的low memory killer.

ULMK ‐VMPRESSURE

这个lowmemorykiller实现架构解决了上面lowmemorykiller kernel driver实现的不足。

基本工作思路:去掉了以前内核态杀进程的策略部分实现,只监听kernel的vmpressure events, 然后由lmkd根据进程adj以及内存level来决定杀哪些进程。

和以前的lmkd行为相比有个变化:

  • lmk仅根据oom_score_adj值的大小选择杀进程,而不会考虑进程本身占用内存的大小
  • native进程的oom_score_adj的值由rc文件设置或者继承自父进程,一般都是静态的,不会变化. native进程的oom_score_adj都小于0,其中很多重要的系统支撑进程的oom_score_adj值为 -1000,oom killer都杀不了

lmk默认只管理oom_score_adj大于等于0的进程,所以只能杀死apk进程。

由这个 变化可以看出:lmkd杀进程的选择策略已经跟内核慢慢脱离关系了,完全是用户态决定了。

具体:

  1. AMS 与 Lmkd 通过soket通信。

  2. Lmkd 与 kernel的Memcg通信

    a. kernel 会向 lmkd 报告memory pressure event
    b. Lmkd 会根据kernel报告的critical memory pressure 或medium memorypressure 杀app
    c. 根据进程的adj以及minfree来选择app杀掉

Lmkd 监听vmpressure

vmpressure本身就定义了low, medium, critical三类内存压力状态,

vmpressure本身就定义了low, medium, critical三类内存压力状态,
LMKD 的初始化流程:

‐> init_mp_common(low,medium,critical)
open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY | O_CLOEXEC);
evctlfd = open(MEMCG_SYSFS_PATH "cgroup.event_control", O_WRONLY | O_CLOEXEC);
write(evctlfd, buf, strlen(buf) + 1)
‐> memcg_write_event_control
event‐>register_event = vmpressure_register_event;
event‐>register_event(memcg, event‐>eventfd, buf);

Lmkd 如何处理vmpressure

对于 memory pressure 事件,处理函数是 mp_event_common,传递给他的 data 是memory

pressure level
Lmkd::init
‐>init_mp_common
vmpressure_hinfo[level_idx].data = level_idx;
vmpressure_hinfo[level_idx].handler = mp_event_common;
epev.data.ptr = (void *)&vmpressure_hinfo[level_idx];
ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, evfd, &epev);

在未开启use_minfree_levels的情况下,需要结合mp level以及swap 分区空余大小来决定是否kill app,

kill app,
‐>!use_minfree_levels
if (mi.field.nr_free_pages < low_pressure_mem.max_nr_free_pages) {
pages_to_free = low_pressure_mem.max_nr_free_pages ‐
mi.field.nr_free_pages; }
if (level < VMPRESS_LEVEL_CRITICAL &&
mi.field.free_swap > low_pressure_mem.max_nr_free_pages)
return;
min_score_adj = level_oomadj[level];
find_and_kill_processes(min_score_adj, 0);

Issues with vmpressure for memory pressure detection

1:Reflects current reclaim efficiency rather than memory pressure level

从小米的psi工作文档里面的Documentation/cgroup-v1/memory.txt里面的vmpressure描述,能够看出来这个确实是反映的是current reclaim efficiency。

2:Difficult to tune because of no direct link between reclaim efficiency and its effects on user experience。

这一点很重要,Android上的lowmemory killer的运用,主要是解决用户操作手机卡顿,改善用户体验的。但是这个卡顿和内存回收效率并无直接关系,需要呼唤psi了。

3:Tightly coupled with vmscan implementation, changes in vmscan

mechanisms may result in behavior change

4:In testing, highly depends on the system memory size and particular

workload.

ULMK‐‐PSI

PSI improvements

  • More accurate pressure detection compared to vmpressure (2‐10x fewer false positives)

google在Android Q上已经基于PSI改进了lmkd,可以更加及时地检测到内存过载和进行回收.根据google的内存压力测试,新机制的检测准确率超过上面的vmpressure的十倍。

  • Thresholds are configurable making tuning possible
  • PSI signals are rate‐limited and userspace can decide how often to poll after the first signal
  • Supports unlimited number of triggers

PSI解释

PSI将各个任务延迟汇总为资源压力指标,这些指标反映工作负载运行状况和资源利用率方面的问题。

基准productivity:可以在CPU上执行任务的时间。

压力:表示由于资源争用而无法执行任务的时间量。

productivity的概念包括两个部分:workload和CPU。 为了衡量压力对两者的影响,我们定义了两个

资源的争用状态:SOME和FULL。

  • SOME: Time percentage due to the stalling of a few tasks caused by lack of a specific kind of resource.
  • FULL: Time percentage due to the stalling of all tasks caused by lack of a specific kind of resource.

Psi旨在提供可供用户配置的低延迟的短期压力监测机制。 在用户定义的时间窗口内度量延迟高于用户定义的阈值时通知用户。

时间窗口和阈值都以usecs表示,可以同时监视具有不同阈值和窗大小的多个psi资源,PSI监测的资源包括:memory,IO以及cpu

PSI如何监控资源压力

用户需要注册trigger,当资源压力超过门限值时通过poll()通知用户。

  • Trigger描述的是特定时间窗口内的最大累计停顿时间,例如 任何500ms窗口内100ms的总停顿时间生成唤醒事件
  • 要注册trigger,用户必须在proc / pressure /下打开psi接口文件,表示要监视的资源,并写入所需的阈值和时间窗口。例如,将“some 150000 1000000”写入/ proc / pressure / memory,将为在1秒时间窗口内测量的部分内存停顿150ms阈值。
  • PSI监视器仅在系统进入受监测的psi度量标准的卡顿状态时激活, 并在退出卡顿状态时停用。

系统记录memory stall状态

系统通过psi_memstall_enter以及psi_memstall_leave记录memory stall 状态,

在下面几个内存相关的文件将对memstall 状态进行记录

mm/compaction.c
mm/filemap.c, mm/page_alloc.c mm/vmscan.c

Psi 更新trigger

‐>psi_memstall_enter/psi_memstall_leave
‐>psi_task_change
‐>psi_schedule_poll_work
‐>psi_poll_work
‐>collect_percpu_times
‐>update_triggers
growth = window_update(&t‐>win, now, total[t‐>state]);
if (growth < t‐>threshold)   //比较窗口时间内的stall时间是否> threshold
continue;
if (cmpxchg(&t‐>event, 0, 1) == 0)
wake_up_interruptible(&t‐>event_wait);  //产生event,唤醒等待队列进程

通知事件

psi_fop_poll此函数是用户态发起poll()系统调用后,会有psi此函数对接,检查在此poll之前的时间内是否有事件发生,如果有则设置相应事件signal,如果无则通过poll_wait()让其等待。所以用户每次监听io/mem/cpu的任一文件,都会引发此对接函数的调用,根据已有的trigger判断事件监听情况。

Lmkd 处理PSI通知

处理函数依然是mp_event_common

mp_event_common
‐>zone_watermarks_ok
‐>file_cache_to_adj
‐>find_and_kill_process

不再使用use_minfree_levels, 通过zone 水线判断是否需要kill process, 若需要kill则通过file_cache_to_adj来获取adj.

Android现有ulmk+psi的不足

有个问题,psi的初衷跟我的单线程工作性能解析初衷是一样的,想衡量单线程在cpu,mem和io这三大块资源的等待延迟时间。
所以很好,以后我的单线程工作性能解析,进一步地发展就是跟psi靠拢吧。

但是psi上传的事件信息,很难看出来是哪个task延迟的,这样不好吧,那还不如做成mem cgroup分组后,这样最起码知道不同的mem cgroup的psi值状况。

例如知道前台group里面有psi内存压力导致该group内的task卡顿时,就去有选择地杀掉后台group里面的进程。

psi上传信息为什么不带上task的pid一点分析:

这是个需要调查的问题点,如果真带上的话,哇,那就完美了,我的单线程工作性能解析以后就有救了,可以利用psi信息,就知道解压缩线程的工作性能状况了。
不过要想做到这个,还得加上mem psi的具体卡在什么地方的信息(mem reclaim, mem thrashing, mem compact or others)。
这样还不如用那个kernel里面tools目录下面的get_task_delay工具了。

所以最后总结:psi这样设计上报用户态接口信息,是有依据的,只是上报下统计数据信息,不想上报内存压力各阶段耗时,因为上报信息多了,就会对内核性能产生干扰了

标签:psi,内存,memory,进程,Android,adj,event
From: https://www.cnblogs.com/linhaostudy/p/18577320

相关文章

  • Android SeekBar(ProgressBar) 常用设置
    AndroidSeekBar常用设置滑块设置android:thumb属性用来指定滑块的样子,同时会影响整个进度条的尺寸进度条设置android:progressDrawable属性用来控制整个进度条的样式,最好是drawable[layer-list],可以分别指定背景,二级进度条,一级进度条。即background,secondaryProgress,progr......
  • 如何解决无法将视频从 iPhone 发送到Android问题
    在数字时代,智能手机已成为我们日常生活中不可或缺的一部分,用于捕捉和分享珍贵的时刻。但是,当尝试将视频从iPhone发送到Android设备时,您可能会遇到错误“无法将视频从iPhone发送到Android”。遇到这种情况应该怎么办?不用担心;本文旨在深入探讨此问题背后的原因,提供多种解决......
  • C语言(十)---- 数据在内存中的存储
    数据在内存中的存储整数在内存中的存储整数的二进制的三种表示方法分别是原码,反码,补码对于整形来说,数据存放内存中存放的是补码,原因是补码可以将符号位和数值位统一处理,加法减法也是一样(cpu只有加法处理器)好处是:使用补码可以将符号位和数值域统一处理,并且因为原码和补码......
  • GreatSQL内存消耗异常排查攻略:从系统到应用层面的深入分析
    GreatSQL内存消耗异常排查攻略:从系统到应用层面的深入分析当GreatSQL数据库处于高并发高负载时,可能会发现mysqld进程的内存消耗远远超出设置的innodb_buffer_pool_size时,有时候甚至会高达甚至超过系统内存的90%,遇到这种问题时,心里经常会发慌,担心下一秒内存就会爆了发生OOM......
  • 轻松理解操作系统 - 一文讲明 共享内存
    大家好,我是菲英。在前面呢,我们了解了进程间通信模块的管道机制和消息队列机制:管道机制是什么?消息队列是什么?本期,我们就继续来了解进程间通信模块的共享内存机制。首先,为什么它是最快的进程间通信机制?因为,它是多个进程的一块虚拟内存映射到同一块物理内存区域来实现数据共......
  • Android 11.0 mtk平板camera2横屏预览旋转90度功能实现
    1.前言在11.0的系统rom定制化开发中,在进行一些平板等默认横屏的设备开发的过程中,需要在进入camera2的时候,默认预览图像也是需要横屏显示的,所以就需要看下mtk的camera2的相关预览功能,然后看下进入launchercamera的时候看下如何实现预览横屏显示如图所示:2.mtk平板camera2......
  • 内存映射文件
    内存映射文件前言:理解。​​‍内存映射文件(Memory-MappedFiles)是指操作系统向上层程序员提供的功能(系统调用)方便程序员访问文件数据方便多个进程共享同一个文件‍一、访问文件数据的使用对比:​​​​理解:一个是访问磁盘,一个是访问内存(OS帮你访问磁盘),后者从I/O......
  • 虚拟内存的基本概念
    虚拟内存的基本概念‍​​​​‍一、传统存储管理方式的特征、缺点​​一次性:作业必须一次性全部装入内存后才能开始运行。这会造成两个问题:作业很大时,不能全部装入内存,导致大作业无法运行。当大量作业要求运行时,由于内存无法容纳所有作业,因此只有少量作业能运行,导......
  • 内存的基础知识
    内存的基础知识‍​​‍一、存储单元​​‍二、指令的工作原理重点:区分物理地址和逻辑地址​​‍三、从写程序到程序运行​​‍四、链接的三种方式(一)静态链接在程序运行之前,先将各目标模块及它们所需的库函数连接成一个完整的可执行文件(装入模块),之后不再拆开(二)......
  • 内存管理的概念
    内存管理的概念‍​​‍一、​​‍二、内存保护(一)方法一:设置上下限寄存器​​‍(二)方法二:设置重定位寄存器(基址寄存器)和界地址寄存器(限长寄存器)​​......