LWN: Android内存管理

栏目: IOS · Android · 发布时间: 5年前

内容简介:ByAndroid系统的设计核心目标就是要能够让用户体验到丝滑一般的响应速度,哪怕是CPU和内存很有限的场景,体验也不能下降的太离谱。为了达成这个目标,就引入了很多的技术细节,包括定期进行low-memory process killer(低空闲内存时杀进程)这个其他系统下没有的功能。在2019 Linux Storage, Filesystem, and Memory-Management Summit上,Suren Baghdasaryan介绍了Android为了让UI交互进程(interactive

Android memory management

By Jonathan Corbet,  May 1, 2019, LSFMM

Android系统的设计核心目标就是要能够让用户体验到丝滑一般的响应速度,哪怕是CPU和内存很有限的场景,体验也不能下降的太离谱。为了达成这个目标,就引入了很多的技术细节,包括定期进行low-memory process killer(低空闲内存时杀进程)这个其他系统下没有的功能。在2019 Linux Storage, Filesystem, and Memory-Management Summit上,Suren Baghdasaryan介绍了Android为了让UI交互进程(interactive processes)能有足够的内存,碰到了哪些问题。

Baghdasaryan一开始就介绍了最近新加的pressure-stall information (https://lwn.net/Articles/759781/)  功能。这个功能起初不是Android开发的,不过看起来非常有效。它能让Android runtime拿到更精确的内存压力信息,也就能用来更好的管理运行的那些进程。总的来说,Android memory management的目标是能让interactive process尽可能的正常运行,与此同时也减少不必要的out-of-memory kill事件(也就是希望能少kill进程)。

Android的low-memory killer daemon (LMKD) 负责来实现这些功能。除了pressure-stall information(内存卡顿压力信息),近期还有其他一些功能能让LMKD更有效。例如pidfd( LWN:给进程发signal的时候PID已经被别的进程占用了,怎么办?! )就是很有用的技术,还有正在加入的功能可以轮询这些descriptor来检测死亡进程的也会很有用处。但是在回收内存的时候,还是有不少问题。例如cgroup,这个功能在其他很多地方都很有帮助,但是在LMKD里面,却事实上把kernel的LRU(least-recently-used,最近使用过)list给分成了很多的小list,这样内存回收实际上更难了。这个session讨论的核心问题就是怎样能尽快从LMKD杀死的进程那里把内存回收出来。内存回收(reclaim)有时候可能会耗费很长时间,甚至是无法预期什么时候才能回收到可用内存,这样LMKD就很难使用了,它甚至会很快开始继续kill其他本不用被kill的进程。Baghdasaryan的opportunistic reclaim patches ( LWN:尽快释放被杀死的进程内存 )就是希望能改善这个情况。它能够马上把被kill的进程(目标进程)的内存剥夺出来,避免目标进程自己出于某些原因只在缓慢的释放资源。快速回收内存就能让LMKD更好的预测是否需要kill更多进程。

第一版的patch是基于OOM reaper代码的,但他认为可能不是最终版的实现方式。不过要想实现出最终版本,需要先回答好几个问题。第一,从被kill的进程哪里剥夺内存,应该在哪里来做?一个方案是让发出SIGKILL信号的进程来负责(在kernel space)回收这部分内存。这个方案有很多好处:很简单能实现,CPU time会算在发起kill的进程上,内存回收时能够自动做好进程优先级继承,也能让user-space更好的控制内存回收的可预期性,但是从超大进程(large processes)来回收内存可能存在一些问题。

第二个方案就是找一些kernel thread来做这个事情,这样能让API很简单,也能做到很容易扩展(针对那些超大进程)。但是呢,user space就没法控制这些内存回收了。

Rik van Riel注意到这里有一个硬件配置问题,某些时候,移动设备会运行的非常快,也拥有足够的内存,这样根本也就不需要内存回收。这种场景下,增加一个新的API来加速内存回收可能本身就是一个错误的行为。Michal Hocko却认为这里的真正问题在于被block的进程(它们因此没法做自己的cleanup工作来释放内存),而不在于这些硬件配置是否高端。Johannes Weiner提出,针对那些硬件资源有限的环境,也可以通过自动把推出的进程迁移到系统里最快的CPU上去做。其他的资源限制就不会对这个正在退出的进程造成阻碍了,它们也就能尽快让出资源,因此他认为这里可能真正应该做的是调整CPU的分配。其他还有一些人担心这个方案会导致功耗过高的问题,不过其实这种场景下一般是有interactive process正在进行交互,这样一般都是会有一个快速的CPU(例如ARM里的big cores)正在运行的。

Hocko回答道,这里用到的功耗并不是一个问题,但是进程隔离可能是个问题。如果一个进程被绑定到一个slow CPU上,那么把它搬到fast CPU上去很可能会破坏cgroup之前规定好的进程隔离的规则,进而影响到当前正在运行的interactive processes交互进程。如果真的希望让一个正在退出的进程迁移去一个fast CPU上去做自己的清理释放工作,那就应该是让user space来发起这个迁移,把它移到另一个cgroup里去,然后再kill。

Mel Gorman提出,这里会有好几个问题,一个是可能没有足够的CPU time来让这个清理释放工作尽快完成;另一个问题是进程空间现在已经膨胀的太大了,哪怕是最快的CPU也很难立刻把清理释放工作即可完成。他认为最好的方案很简单,至少是kernel这部分很简单:正在退出的进程应该迁移到一个快速CPU上(当然前提条件是CPU mask允许它迁过去),剩下的工作就是user space该解决的了,它应该把进程事先迁移安排好,如果它真想尽快回收内存的话。kernel不应该做更多的事情了,否则肯定会违背某些应用场景下进程隔离的需求。

Matthew Wilcox回到最开始的方案,他觉得在发起kill的进程那里去回收内存,就能绕过这些问题了。Gorman回答道,按那样去实现内存回收的话肯定能减少CPU之间的inter-processor interrupt终端,因为被kill的进程的内存不用被两个CPU都来touch了(那样性能肯定会不好)。也有人担心如果让发起kill的进程来负责回收的话,kill()系统调用就可能会变成一个blocking operation(阻塞操作)了,没人知道kill()多久之后才会返回,这样发起kill()的进程会丈二和尚摸不着头脑。

一个最终方案可能就是利用madvise(MADV_DONTNEED)类似的操作,来允许某个进程可以强制回收另一个进程的内存(至少是部分)。Gorman有点担心这个操作可能会被利用来做恶作剧,就像常说的”进程是否能用ptrace()来强制控制目标进程?“。不过这个API可能会有个好处就是能用在多线程的环境里,每个线程可以释放一部分地址空间。这样就能比较容易的把释放内存的工作并行化。在讨论的最后,还有个建议,每当对一个pidfd(process file descriptor)来调用fadvise()或者truncate()的时候,就能够做到回收目标进程的内存。不过这个idea没有更多时间展开讨论了。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

刘强东自述

刘强东自述

刘强东 / 中信出版集团 / 2016-6-1 / 49.00

京东 1998年,京东还只是中关村一个经营光磁生意的小柜台,月营业额仅有几万元,如今则已经成长为中国营收规模超大的互联网企业,2015年全年营收1813亿,总交易额达到4627亿元; 为解决电商“最后一公里”的痛点,创立并自建B2C物流模式; 经常被争议,却始终坚持“不挣快钱”,选择上市不是因为“缺钱”,只为让合作伙伴睡得着觉,为用户和社会创造价值,由此成就让整个华尔街一片京东红......一起来看看 《刘强东自述》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具