1 27
Linux内核学习:内存管理

理解Linux内存管理能够更好地理解进程的虚拟空间排布(当一个进程需要某块内存时,它会把自己的一个内存地址交由OS处理)。

虽然进程只关心如何使用自己的线性排列的虚拟地址,而不需要关心物理内存的实际容量,以及如何使用真实的物理内存,但内存管理是内核最复杂的同时也是最重要的一部分;它是协作处理器与内核的关键流程,所以本文我们主要介绍Linux内存管理的相关知识点。

典型的进程内存排布如下:

process mem

操作系统有保护措施,阻止堆和栈的越界行为发生

内存管理

Linux把物理内存划分三个层次来管理

1、存储节点(node)

2、管理区(zone)

3、页(page)

内存管理涉及的领域:

  • 内存中物理内存页(内存管理的基本单位)的管理

  • 分配大块内存的伙伴系统

  • 分配小块内存的slab分配器

  • 分配非连续内存块的vmalloc机制

  • 进程的地址空间

NUMA计算机

NUMA(非一致性内存访问):多处理器计算机系统的各个CPU都有自己的本地内存;NUMA是稀疏且不连续的内存模型,在给定不同内存单元的访问时间可能不一样,系统的物理内存被划分为几个节点(node),在一个单独的节点内,任一给定CPU访问页面所需的时间都是相同的。

分页管理

分页是为了实现非连续分配,以便解决内存碎片问题;由于程序存在局部化特征,这意味着在特定时间内只有部分内存会被频繁访问,具体点说,进程空间中的text段(代码段)、堆、栈、共享库都是进程空间的某个特定部分,这样会导致进程空间是非常稀疏的,于是从硬件层面开始,页表实现采用分级页表

在说明页表前,我们先介绍几个重要概念:页、页框、页帧

  • 页:本质上是数据块,是信息的物理单位(段才是逻辑单位),是为了更高效和更经济的管理内存、线性地址被分为固定长度单位的组。

  • 页框:本质上是存储区域,是内存存储单元,也是主存的一部分(可用于任何事情:存放内核数据、缓存数据、用户数据等);分页单元把所有的RAM分为固定长度的页框,每个页框包含一个页。

  • 页帧:代表系统内存的最小单位,对内存中的每个页都会创建页描述符 struct page 的一个实例。页描述符存放在mem_map中,是内核中能区分哪些页框包含哪些进程,而哪些页框包含的是内核代码或内核数据。

层次化的页表是个映射表的概念,即从进程的线性地址映射到存储器的物理地址;主要用于支持大地址空间的快速、高效管理

页表也用于用户进程的虚拟地址空间和系统物理内存(内存、页帧)之间的关联,向每一个进程提供了一致的虚拟地址空间,还可以在不不额外增加物理内存的情况下,将页换出到块设备来增加有效的可用内存空间。

Linux内核通过四级页表把虚拟内存空间分成5部分(4个页用于选择页,1个索引用于页内偏移)

process mem

内存区管理

Linux因为必须处理80X86体系结构,所以在硬件上限制了页框的使用方式,这中限制主要是由于硬件存在缺陷而引起的内存寻址问题。为了应对这种限制,Linux 2.6把内存节点分为几个区:

  • ZONE_DMA:低于16MB的页框

  • ZONE_NORMAL:高于16MB低于896MB的页框;大多数内核的操作只操作这个区域,系统内存由很多固定大小的内存块组成,这样的内存块叫“页”

  • ZONE_HIGHMEM:高端内存;高于896MB的框,在32位系统中,896MB边界上的页框不映射在内核线性地址空间的第四个GB,因此内核不能直接访问它们,不过64位系统不存在这个问题

当内核调用一个内存分配函数时,必须指明请求页所在的管理区

内核可以采用三种不同的机制将页框映射到高端内存(ZONE_HIGHMEM)中:

  • 永久内核映射(可能阻塞进程)

  • 临时内核映射(不阻塞进程)

  • 非连续内存分配

非连续内存管理

把内存映射到一组连续的页框是最好的选择,这样会利用高速缓存并获得较低的平均访问时间。不过对于内存区的请求不是很频繁,那么通过连续的线性地址访问非连续的页框这样的一种分配方式会很有意义。

优点:避免外碎片

缺点:打乱内核页表

非连续内存管理的主要用途有三点:

  • 为活动的交换区分配数据结构

  • 为模块分配空间

  • 给某些I/O驱动程序分配缓冲区

初始化

在启动过程期间,尽管内存管理尚未初始化(初始化后,交由伙伴系统承担),但内核仍然需要分配内存以创建各种数据结构,这个过程通过bootmem进行分配

bootmem作用于伙伴系统前,是在启动阶段的早期分配内存的,在需要分配内存时,它会逐位扫描位图(每次分配都必须从头扫到尾),然后每次通过对内存进行线性搜索实现分配,直至找到一个能提供足够连续页的位置,这个过程也叫 first-best。

伙伴系统

伙伴系统算法(fit-best)是内核为分配一组连续的页框而建立的一种分配策略,这个算法可以避免外碎片

伙伴系统的原理主要是通过一种技术纪录现存的空闲连续页的情况,以尽量为满足对小块的请求而非分隔打的空闲块

外碎片:指系统中无法利用的小存储块。

内碎片:指分配给作业的存储空间中未利用的部分

伙伴系统分配方案是一个结合2的方幂个分配器和空闲缓冲区合并计算的内存分配方案,它的基本思想是:把内存划分为很多不同页面规格的大块,当向内存申请特定大小的块时,会判断是否存在该大小的块,有则分配,无则把更大的块分为两部分,一部分进行分配,一部分处于空闲,用于以后的分配。当一个块被最终释放时,其伙伴将被检测出来,如果伙伴也处于空闲则被合并两者

slab

伙伴系统采用页框作为基本内存区,这适合对大块内存的请求,那么小块内存呢?这里我们使用slab分配器。

slab说白了就是一种空间换时间的技术,它把对象分组放到高速缓存中。包含高速缓存的内存区被划分为多个slab,每个slab由一个或多个连续页框组成,这些页框既包含分配对象,页包含空闲对象。

slab分配器所管理的对象可以在内存中对齐,通常是2的倍数,这个倍数称为对齐因子。对齐因子允许最大的范围是4096,4k页就是页框的大小。

内核使用 map_vm_area将分散的物理内存页连续映射到虚拟的vmalloc区域

提供小块内存不是slab分配的唯一任务,它也作为一个缓存,这个缓存主要针对经常分配与释放的对象。

使用slab有两大好处:

一、调用伙伴系统的操作对操作系统的数据和指令的高速缓存有相当的影响。更轻量的slab在可能的情况下减少了对伙伴系统的调用。

二、如果数据存储在伙伴系统直接提供的页中,那么其地址总是出现在2的幂次整数倍附近。这对CPU高速缓存有负面的影响,由于这种地址分布,使得某些缓存行过度使用,而某些则几乎为空。通过slab着色,slab分配器能均匀地分布对象,以实现均匀的缓存利用。