共计 1960 个字符,预计需要花费 5 分钟才能阅读完成。
本篇从我本人的角度来写对物理内存治理的了解。因为 Linux 引入了虚拟内存的概念,应用程序对物理内存的拜访都是由内核模块来接管的,因而带着以下问题,逐渐揭开相干的细节:
- 内核是应用什么地址拜访物理内存的?
- 物理内存为何须要分区?
- 搭档零碎和 SLAB 零碎 有何区别?
页框治理
想要治理内存,首先要晓得有哪些内存,并且把内存状态记录下来。物理内存默认以 4k 宰割为一个个的单元,每个单元被称为页框(page frame)。内核应用 struct page
数组跟踪内存中每个页框的以后状态。数组的每个元素对应于物理内存中的一个页框,数组定义如下:
// `struct page` 定义在 `linux/mm_types.h`
struct page *mem_map;
例如,mem_map[0] 蕴含内存中第一个页框的信息
名词阐明:
- 页框: 存储数据的内存块
- 页:寄存在页框内的数据块
如此,内核就通过页框数组把所有的内存应用索引了起来,并且晓得每个页的状况,例如:是否闲暇、拥有者是谁。
为什么分区?
然而对于内核来说仅有分页是不够的,内核也没方法 齐全 间接拜访内存,是什么起因呢?
具体还是要从内存调配过程聊起来。过程申请内存的时候,会调用 malloc() 和 mmap() 等内存调配函数,最终会发动零碎调用陷入内核态进行内存调配。然而,内存调配过程调配的只是虚拟地址空间,并没有给虚拟内存调配对应的物理内存。当过程拜访没有建设映射关系的虚拟内存时,将触发一个缺页中断。当一个过程产生缺页中断的时候,过程会再次陷入内核态,查找 / 调配一个页框,建设映射关系(虚拟地址到物理地址)
能够看到过程在调配内的时候两次进入内核态,然而两次却齐全不同。要了解这一点首先要相熟两个概念“过程上下文”vs“中断上下文”
在 Linux 实现中,处理器在执行过程中总是处于以下三种状态:
(1)内核态,运行于过程上下文,内核代表过程运行于内核空间。
(2)内核态,运行于中断上下文,内核代表硬件运行于内核空间。
(3)用户态,运行于用户空间。
内核的地址空间不仅仅要反对硬件拜访,同时还须要映射到过程的虚拟地址空间,成为过程上下文的一部分。
当然,独自从实现来看,对于(1)、(2)两种状况,内核的上下文如果完完全全从过程上下文独立开也是可行的,甚至更为简略。然而从性能来看,以后的计划才是更优的。详情参考:《User Space on Top of Kernel Space Versus Separated Address Spaces》
分区地址映射
32 位零碎中,内核模块的地址空间只有 1G。然而,内核又要拜访所有的 4G 内存。但内核拜访物理内存与过程拜访虚拟内存不同,虚实映射既耗费空间也耗费性能(详见:地址映射),且在内核场景下,内存挪动与内存换出的需要并不高,也没有多过程隔离的需要(详见:内存共享),映射的收益不大。
因而,内核把页框分组,划分为不同的区(ZONE)。内核空间的前 896MB(不仅是内核代码,还有它的数据)被“间接”映射到物理内存。虚构内核空间的最初 128MB 局部被映射到物理“高内存”(> 896MB)的一些局部。物理内存的间接映射容许物理页面分配器的间接拜访取得的页面,而无需任何映射操作。获取物理页的虚拟地址所需的惟一操作是增加固定偏移量。
通过以上形式,既实现 4G 内存的拜访,也保障了内核拜访的性能。最终,物理内存的页框就被组织成了以下的模式
从内核地址空间虚实转换的视角来看,如下:
内存分配器
对于闲暇内存的调配治理是交给内存分配器进行的。内核中有两种内存分配器,即搭档零碎分配器 和 SLAB 分配器。前者是页框分配器,后者是对象分配器。
搭档零碎的引入为内核提供了一种用于调配一组间断的页而建设的一种高效的调配策略。防止因频繁地申请和开释不同大小的间断页框,导致在已调配页框的内存块中扩散了许多小块的闲暇页框,而其余须要调配间断页框的申请无奈失去满足。
SLAB 工作是针对一些常常调配并开释的对象,如过程描述符等内核中常见的小对象。如果间接采纳搭档零碎来进行调配和开释,不仅会造成大量的内碎片,而且处理速度也太慢。而 SLAB 分配器是基于对象进行治理的,雷同类型的对象归为一类(如过程描述符),每当要申请这样一个对象,就从一个 SLAB 列表中调配同样大小的内存,而当要开释时,将其从新保留在该列表中。
搭档零碎解决了内存内部碎片问题,而 SLAB 解决了内存的外部碎片问题。所谓内部碎片是指因为频繁地申请和开释页框而导致的某些小的间断页框,而外部碎片就是指被调配进来然而不能被利用的内存。
两个零碎的细节临时按下,后续详聊。
本文作者 :cyningsun
本文地址 :https://www.cyningsun.com/06-…
版权申明:本博客所有文章除特地申明外,均采纳 CC BY-NC-ND 3.0 CN 许可协定。转载请注明出处!