持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第27天,点击查看活动详情
伙伴系统以页面为单位来管理内存,内存碎片也是基于页面的,即由大量离散且不连续的页面组成的。从内核角度来看,出现内存碎片不是好事情,有些情况下物理设备需要大段的连续的物理内存,如果内核无法满足,则会发生内核错误。对于内存碎片化,需要重新规整一下,因此本节叫作内存规整,一些文献中称其为内存紧凑,它是为了解决内核碎片化而出现的一个功能。 内核中去碎片化的基本原理是按照页面的可移动性将页面分组。迁移内核本身使用的物理内存的实现难度和复杂度都很大,因此目前的内核不迁移内核本身使用的物理页面。对于用户进程使用的页面,实际上通过用户页表的映射来访问。用户页表可以移动和修改映射关系,不会影响用户进程,因此内存规整是基于页面迁移实现的。
内存规整的基本原理
系统长时间运行后,页面变得越来越分散,分配一大块连续的物理内存变得越来越难,但有时系统就是需要一大块连续的物理内存,这就是内存碎片化(memory fragmentation)带来的问题。内存碎片化是操作系统内存管理的一大难题,系统运行时间越长,则内存碎片化越严重,最直接的影响就是分配大块内存失败。 在Linux 2.6.24内核中集成了社区专家Mel Gorman的Anti-fragmentation补丁,其核心思想是把内存页面按照可移动、可回收、不可移动等特性进行分类。可移动的页面通常是指用户态程序分配的内存,移动这些页面仅仅需要修改页表映射关系,代价很低;可回收的页面是指不可以移动但可以释放的页面。按照这些类型分类页面后,就容易释放出大块的连续物理内存。 内存规整机制归纳起来也比较简单,如图5.17所示。有两个方向的扫描者:一个从zone头部向zone尾部方向扫描,查找哪些页面是可以迁移的;另一个从zone尾部向zone头部方面扫描,查找哪些页面是空闲页面。当这两个扫描者在zone中间碰头或者已经满足分配大块内存的需求时(能分配出所需要的大块内存并且满足最低的水位要求),就可以退出扫描了。内存规整机制除了人为地主动触发以外,一般在分配大块内存失败时使用。首先使用内存规整机制尝试整理出大块连续的物理内存,然后才使用直接页面回收机制。这好比旅行时若发现购买了太多的东西,那么我们通常会重新规整行李箱,看是否能腾出空间来。
触发内存规整
Linux内核中触发内存规整有3个途径。 手动触发。通过写1到/proc/sys/vm/compact_memory节点,会手动触发内存规整。它会扫描系统中所有的内存节点上的zone,对每个zone都会做一次内存规整。 kcompactd内核线程。和页面回收kswapd内核线程一样,每个内存节点会创建一个kcompactd内核线程,名称为“kcompactd0”“kcompactd1”等。 直接内存规整。和页面回收一样,当页面分配器发现在低水位情况下无法满足页面分配时,会进入慢速路径。在慢速路径里,除了唤醒kswapd内核线程外,还会调用函数__alloc_pages_direct_compact(),尝试整合出一大块空闲内存。