从Android9.版本开始,系统放弃了传统的Lowmemorykiller改用LMKD(LowMemoryKillerDaemon)进行低内存查杀。
从Android10版本开始,lmkd的监测内存模式从vmpressure变为了PSI方式。
9.0之前的版本主要依靠以下位置的文件进行判断
#/sys/module/lowmemorykiller/parameters/minfree18432,23040,27648,32256,55296,80640#/sys/module/lowmemorykiller/parameters/adj0,100,200,300,900,9061.2PSI代替vmpressure**?**vmpressure信号(由内核生成,用于内存压力检测并由lmkd使用)通常包含大量误报,因此lmkd必须执行过滤以确定内存是否真的有压力。这会导致不必要的lmkd唤醒并使用额外的计算资源。使用PSI监视器可以实现更精确的内存压力检测,并最大限度地减少过滤开销。
支持LMKD需要的编译配置
CONFIG_ANDROID_LOW_MEMORY_KILLER=nCONFIG_MEMCG=yCONFIG_MEMCG_SWAP=y支持PSI需要的编译配置
CONFIG_PSI=y所以在Android10及以上的版本中,当系统初始化完成后启动lmkd之后,会首先判断是否use_inkernel_interface(高版本都是false),然后判断是否支持PSI,不支持则用vmpressure。然后在根据是否是低内存设备和是否用use_minfree_levels采用不同的策略。
lmkd.rc
servicelmkd/system/bin/lmkdclasscore//核心进程class_startcoreinit.rc中onbootuserlmkdgrouplmkdsystemreadproccapabilitiesDAC_OVERRIDEKILLIPC_LOCKSYS_NICESYS_RESOURCEcritical//4min之内crash4次,则重启bootloadersocketlmkdseqpacket0660systemsystem//设置socketwritepid/dev/cpuset/system-background/tasks//对应cpusetcritical的具体代码参考system/core/init/service.cpp
lmkd通过试探进入内核lmk模块路径(/sys/module/lowmemorykiller/parameters/minfree)的方式判断当前系统是否含义lmk模块。如果存在内核lmk模块,并且用户配置了enable_userspace_lmk为false,直接使用内核lmk。否则使用用户空间lmkd。
在init_monitors()(判断通过psi还是vmpressure检测内存,我这里的设备Android10以上均为psi方式。
只有当设备不是低内存设备,同时使用minfree级别时,不使用新策略。
psi主要监控了proc/pressure下iomemorycpu三项指标。
init_psi_monitor
register_psi_monitor
lmkd进程的客户端是ActivityManager,通过socket(dev/socket/lmkd)跟lmkd进行通信,当有客户连接时,就会回调ctrl_connect_handler函数>ctrl_data_handler>ctrl_command_handler
//lmkd进程的客户端是ActivityManager,通过socket(dev/socket/lmkd)跟lmkd进行通信,//当有客户连接时,就会回调ctrl_connect_handler函数>ctrl_data_handler>ctrl_command_handler这里我们直接看ctrl_command_handler
mp_event_psi使用zone_watermark监测。当设备为低内存或者不使用旧模式minfree时,均如下处理方式。
非低内存设备并且使用iuse_minfree_levels
代码里表明提高被杀进程的优先级,尽快干掉他
解析中使用了小技巧,zoneinfo为union,因此可以通过遍历zoneinfo_field_names的同时遍历zoneinfo的attr,实现快速解析。在使用时,又可以通过zone的field快速访问。
zoneinfo中多计算了个totalreserve_pages,该值时根据high水线和protection保护页面数量(防止过度借出页面)共同计算得来(high水线+protection选取最大保留页)。
lmkd中计算出来的zoneinfo为总大小,并未区分各个zone
/proc/meminfo信息打印的地方在[kernel/msm-5.4/fs/proc/meminfo.c]的meminfo_proc_show函数当中;其中主要是调用show_val_kb()函数将字符串和具体的数值凑成一个字符串,然后把这些字符串打印出来。
shmem比较特殊,基于文件系统所以不算匿名页,但又不能pageout,因此在内存中被统计进了Cached(pagecache)和Mapped(shmem被attached),但lru里是放在anonlru,因为可能会被swapout。
lmkd的meminfo中也多计算了一个字段nr_file_pages,该值包括cached+swap_cached+buffers。可以理解为能够被drop的文件页。
进程rss信息获取:"/proc/pid/statm"
统计的数据依次为:虚拟地址空间大小,rss,共享页数,代码段大小,库文件大小,数据段大小,和脏页大小(单位为page)。