【Kernel】内存管理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/iEearth/article/details/79350927

1、概述

内存管理是内核最复杂同时也最重要的一部分,具体包括内存中的物理内存页的管理,分配大块内存的伙伴系统,分配较小块内存的slab、slub和slob分配器,分配非连续内存块的vmalloc机制,以及进程的地址空间。
内核一般将处理器的虚拟地址空间分为用户空间和内核空间,在两个用户进程之间切换时会切换它们的上下文,用户空间发生变化,但内核空间保持不变。
可用的物理内存奖映射到内核的地址空间中。管理物理内存,有两种方式,一种是UMA即一致内存访问,将可用内存以连续方式组织起来,SMP系统中的每个处理器访问各个内存区都是同样快;另一种是NUMA即非一致内存访问,总是多处理器计算机,系统的每个CPU都有本地内存,可支持特别快速的访问,各个处理器之间通过总线连接起来,以支持对其它CPU的本地内存的访问,当然比访问本地内存慢些。
内核的内存模型有三种类型可进行选择配置,分别是平坦内存模型FLATMEM、稀疏内存模型SPARSEMEM和不连续内存模型DISCONTIGMEM,默认为平坦内存模型。

2、NUMA

内核对UMA和NUMA系统使用相同的数据结构,UMA系统可以认为是只使用了一个NUMA结点的伪NUMA系统。内存划分为结点,每个结点关联到系统中的一个处理器,各个结点又划分为内存域,包括DMA内存域,可直接映射到内核段的普通内存域,超出内核段的物理内存的高端内存域,以及防止物理内存碎片的伪内存域。
内存域以页为单位进行管理,如IA-32系统的标准页长度为4KB,页在换入换出时涉及一个称为水印的概念,水印用于衡量空闲页的性能。另一个概念是冷热页,热页表示页已经加载到CPU高速缓存,比在内存中的页有更快的数据访问速度,冷页则相反。

3、页表

页表用于建立用户进程的虚拟地址空间和系统物理内存的关联,向每个进程提供一致的虚拟地址空间,应用程序看到的地址空间是一个连续的内存区。页表为多级页表,内核内存管理总是假定使用四级页表,不使用的分级页表需要由特定于体系结构的代码模拟。
根据四级页表结构的需要,虚拟内存地址分为五部分,4个表项用于选择页,1个索引表示页内位置。页表有许多属性,常见的如可读、可写、可执行。

4、初始化

内存管理是内核一个非常重要的部分,在特定于体系结构的设置步骤中检测内存并确定内存的分配情况后,会立即执行内存管理的初始化。
内核定义了内存的一个层次结构,首先试图分配廉价的内存,如果失败,则根据访问速度和容量,逐渐尝试分配更昂贵的内存。高端内存是最廉价的,因为内核没有任何部分依赖于从该内存域分配的内存如果高端内存域用尽,对内核没有任何副作用,这也是优先分配高端内存的原因。普通内存域的情况有所不同,许多内核数据结构必须保存在该内存域,而不能放置到高端内存域,因此如果普通内存完全用尽,那么内核会面临紧急情况,所以只要高端内存域的内存没有用尽,都不会从普通内存域分配内存。最昂贵的是DMA内存域,因为它用于外设和系统之间的数据传输,因此从该内存域分配内存是最后一招。
不同的CPU体系结构,内存管理的初始化工作是不同的。Linux内核在内存中的布局,前4KB是第一个页帧,一般会忽略,因为通常保留给BIOS使用。接下来的640KB原则上是可用的,但也不用于内核加载,其原因是,该区域之后紧邻的区域由系统保留,用于映射各种ROM,通常是系统BIOS和显卡ROM,不可能向映射ROM的区域写入数据。但内核总是会装载到一块连续的内存区,如果从4KB处作为起始地址来装载内核印象,则要求内核必须小于640KB。IA-32内核使用0x100000作为起始地址,这对应于内存中第二兆字节的开始处,从此处开始,有足够的连续内存区,可容纳整个内核。内核占据的内存分为几个断,包括代码断、数据断和初始化的数据断。内核必须保留地址空间的最后128M用于其它目的,如虚拟内存中连续但物理内存中不连续的内存区,持久映射及固定映射。
对于64位系统来说,64位地址空间的跨度太大,当前没有什么应用程序需要这个,因此,一般只实现了一个比较小的物理地址空间,地址字宽度为48位,这在不失灵活性的前提下,简化并加速了地址转换。48位的地址字可以寻址256TB的地址空间。尽管物理地址字位宽被限制在48位,但在寻址虚拟地址空间时仍然使用了64位指针,所以虚拟地址空间的某些部分无法寻址,未来的硬件实现可能支持更大的物理地址空间,但处理器必须隐藏对未实现地址空间的访问。
内核在启动过程期间,尽管内存管理尚未初始化,但内核仍然需要分配内存以创建各种数据结构,bootmem分配器用于在启动阶段早起分配内存,使用了一个位图来管理页,分配原则为最先适配或最先最优,目的在于简单性而不是性能和通用性。在系统初始化进行到伙伴系统能够承担内存管理的责任后,必须停用bootmem分配器。

5、物理内存

在内核初始化完成后,内存管理的责任由伙伴系统承担。伙伴系统基于一个强大的算法,保证了内存分配的速度和效率。
伙伴不必是彼此连接的,如果一个内存区在分配期间分解为两半,内核会自动将未用的一半加入到对应的链表中,如果在未来的某个时刻,由于内存释放的缘故,两个内存区都处于空闲状态,可通过其地址判断其是否为伙伴。有关伙伴系统当前状态的信息可以在/proc/buddyinfo中获得。
内核是反碎片的,从最初开始尽可能防止碎片,所以内核将已分配页划分为三种不同类型,它们是不可移动页、可回收页和可移动页。
伙伴系统在分配内存页时,首先要选择一个页,还要查看这个页的连续性,待分配的页可能是冷热页即CPU缓存中的页,也可能是空闲列表中的页。虚拟内存中连续的页,映射到的物理内存不一定是连续的,这时使用vmalloc进行大内存分配。

6、slab分配器

slab分配器的任务之一是提供小内存块,当然也用作一块缓存。各个缓存管理的对象,会合并为较大的组,覆盖一个或多个连续页帧,这种组称为slab,每个缓存有几个这种slab组成。
slab分配器在大多数情况下工作良好,但对于微型系统来说,代码量和复杂性都太高,于是出现了slob和slub分配器。slob分配器进行了特别优化,以便减少代码量,它围绕一个内存块列表展开,在分配内存时,使用了同样简单的最先适配算法,只有大约600行代码,总的代码量很小,从速度来说,它不是最高效的分配器,也肯定不是为大型系统设计的。slub分配器通过将页帧打包为组,试图最小化所需的内存开销。
slab分配器由一个紧密地交织的数据和内存结构的网格组成,具体由两部分组成,保存管理性数据的缓存对象和保存被管理对象的各个slab。每个缓存只负责一种对象类型,或提供一般性的缓冲区。各个缓存中slab的数目各有不同,这与已经使用的页的数目、对象长度和被管理对象的数目有关。系统中所有的缓存都保存在一个双链表中。
另外,如果不涉及对象缓存,而是传统意义上的分配和释放内存,则必须调用kmalloc和kfree函数,相当于用户空间中C标准库malloc和free函数的内核等价物。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页