第九章 内存管理

内存概述


地址空间


内存单元所对应的实际的地址称为物理地址。通常程序操作访问的内存地址称为逻辑地址,CPU通过总线操作的内存地址称为线性地址。


分页


为了有效管理,物理内存分为固定大小的块,称为帧或页。物理页的大小是由硬件决定的,通常为2的幂,像X86平台为4KB。虚拟地址包含页号

和页内偏移,页号作为页表的索引通过MMU映射到物理内存的基地址,再加上页内偏移就得到完整的物理地址。

分页管理将物理页和虚拟页一一映射,这样可以将连续的虚拟内存映射到非连续的物理页上。此外,固定大小的页面在碎片管理,磁盘交换都有

比较好的优越性。


9.2内核的内存分配


内存结构


Linux的内存结构可以很好支持NUMA(Non-Uniform  Memory Access Architecture)架构的服务器。NUMA下分布式的一个内存节点称为Node,我们

通常用的单机系统为UMA(Uniform Memory Architecture),就是一个Node。

每个Node下物理内存分成几个Zone(区域),Zone内再对物理页进行管理。所以Linux用了Node->Zone->Page这样一个三层的结构来描述物理内存。

由于硬件和体系结构的原因,一些在特定地址范围内的物理页在使用时有所限制。在X86体系结构上,ISA设备的DMA只有24位的寻址能力,也就是

只能访问内存的16MB。而整个系统的最大物理内存寻址可以大于1GB,超过内核的虚拟地址空间。

为此,Linux将X86的物理内存分为三个Zone。

ZONE_DMA(0~16MB)————DMA内存的分配区域。

ZONE_NORMAL(16MB~896MB)—————正常映射的内存区域。

ZONE_HIGHMEN(896MB~)——————高端内存区域,其中的页不能永久映射到内核地址空间,只做动态映射。


BUDDY页面管理


BUDDY算法是一种经典的内存管理算法,其作用是减少存储空间中的空洞和碎片,增加利用率。BUDDY把内存中的所有页面按照2的幂次进行

分块管理,分配的时候如果没有找到相应大小的块,就把大的块二分成小块;释放的时候,回收的块跟相邻的空闲伙伴块又能合并成大块。这就是

BUDDY(伙伴块)名称的由来。


SLAB内存管理


前面我们看到,BUDDY内存分配的时候,是以页块为单位进行的,而且是2的幂次个块。但是多数的内核对象远小于页的大小,而这些对象在操作

系统的生命周期中会被频繁地申请和释放,并且有实验数据发现,这些对象初始化的时间超过了分配内存和释放内存的总时间,所以需要一种更加

细粒度的针对内核对象的分配算法。

Linux引进了SLAB层来进行细致的内存管理。SLAB分配器最早在SunOS(也就是现在的Solaris的前身)中提出和实现。SLAB分配器有诸多优点,可以

进行不同大小的内存对象的分配,实现快速的内存分配和释放,运用着色优化CPU高速缓存,产生内存碎片小等。

SLAB用高速缓存(kmem_cache)来描述不同的内存对象,每种内存对象对应一个高速缓存。高速缓存内由SLAB来管理对象的内存分配。每个SLAB

是一个或多个连续的物理页面,它提供可用的内存对象。


SLUB/SLOB内存管理


SLUB是2.6.22内核引入的一种新的内核对象缓冲区分配器,其特点是简化设计理念,同时保留SLAB分配器的基本思想:每个高速缓存有多个slab组成,

每个slab包含固定数目的内存对象。

SLUB分配器简化了kmem_cache、SLAB等相关的管理数据结构,摒弃了SLAB分配器中众多的队列概念。


vmalloc内存分配


大多数情况下,只有硬件设备需要得到物理地址连续的内存,而仅供软件模块使用的内存块只要虚拟地址连续就可以了。内核为了满足某些大块内存申请

需求,提供了一组函数:

void  *vmalloc(unsigned long size)

void  vfree(void *addr)


函数vmalloc()跟应用层提供的malloc()类似,返回一个连续虚拟地址的内存块,但物理块可能是不连续的。


高端内存映射


前面说到,在X86体系结构中,高于896MB的物理内存范围为高端内存ZONE_HIGHMEN,其物理页不能永久映射到虚拟地址上。

内核提供了两组函数用于高端内存的映射。函数kmap()对高端内存和低端内存都能使用,如果是低端内存直接返回该页的线性映射地址,如果是高端内存

建立一个永久映射并返回其地址。因为地址空间的有限,允许的永久映射是有限的,当内存使用完后需要解除这个映射。

函数kmap()允许睡眠,只能用于中断上下文中。内核另外提供的函数kmap_atomic()是非阻塞的,可以运行于中断上下文中。它预先创建好一组保留的映射,

这样内核可以原子地把一个页面映射到保留的映射中。



进程地址空间


内存描述符


Linux使用内存描述符struct_mm_struct来描述一个进程的地址空间信息。


内存映射


前面我们看到,一个进程运行的时候,其用到文件的代码段、数据段等都是映射到内存地址区域的。这个功能是通过系统调用mmap()来完成的。

SLUB是2.6.22内核引入的一种新的内核


多级页表结构


应用程序的访问对象都是虚拟地址,而我们前面提到CPU通过MMU把虚拟地址转化成物理地址从而访问到真正的物理内存。而虚拟内存和物理内存

的映射关系是存放在页表里的。

内核的设计需要考虑到各种不同的CPU上的实现,还要考虑到在64位CPU上的实现,所以要以一种假想、虚拟的CPU和MMU为基础,设计出一种通用

的模型。为此,提供了一个四层页管理架构,来兼容各种架构的CPU。


缺页错误处理


虚拟内存被分配以后,并没有分配相应的物理页面。内核通过缺页错误处理(PageFault)在对虚拟地址进行一次访问的时候创建页表和分配物理页面。CPU

访问一个虚拟地址时,如果没有查到相应的物理页,就会抛出一个缺页错误的内存异常。


页面缓存


页面缓存,通常也就是文件缓存,使用内存页面缓存文件的逻辑内容,从而提高对磁盘文件的访问速度。

顾名思义,页面缓存是以物理页为单位对磁盘文件进行缓存的。对于Linux以及多数的类UNIX系统,系统一般不会让空闲内存太空闲,而是用作页面缓存,

而在有内存请求时逐步释放缓存。所以一般看Linux的空闲内存总是那么少,其实它们正在为加速访问文件系统做贡献。


页面缓存管理


页面缓存使用结构struct_address_space来描述。通常情况下,页面缓存和一个文件节点inode关联,host域记录了这个页面缓存所属的文件。反之,在文件

节点的描述符struct inode中,有一个域i_mapping记录文件的页面缓存。如果是交换页,host为NULL。


Swap内存交换


交换空间是指在外部存储设备中开辟出来的空间,临时存放从内存中调出的页面。因为用于交换页面,所以也是按页划分管理的。Linux允许使用块设备(比如

磁盘分区)和普通文件两种形式来做交换空间,可以并行管理多个交换空间。

用户可以用mkswap命令创建一个新的交换空间,然后使用swapon/swapoff命令加载或卸载交换空间。

内核中使用数据结构struct  swap_info_struct来描述一个交换空间,允许最多有MAX_SWAPFILES个。


kswapd和pdflush


kswapd用于物理内存回收,以确保系统的空闲物理内存的数量在一个适当的范围。内存页分配中,如果zone的内存数量低于pages_high的时候,kswapd

进入休眠状态。

pdflush负责内存的脏页面的回写,太多的脏页面意味着风险,如果发生故障,内容容易丢失,而突发的大量物理内存请求则需要大量会写来清理内存,这会

导致很不理想的响应时间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值