LWN: Android内存管理

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

内容简介: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没有更多时间展开讨论了。


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

查看所有标签

猜你喜欢:

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

The Web Application Hacker's Handbook

The Web Application Hacker's Handbook

Dafydd Stuttard、Marcus Pinto / Wiley / 2011-9-27 / USD 50.00

The highly successful security book returns with a new edition, completely updated Web applications are the front door to most organizations, exposing them to attacks that may disclose personal infor......一起来看看 《The Web Application Hacker's Handbook》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具