文章目录
一、内存基本知识
早期内存使用方法
- 分段 (汇编中使用.section伪指令可以定义一个新的段,在保护模式中使用描述符描述对应的段)
- 分页(将物理内存分成多个页,每一个段由多个页组成,是最基本的内存管理单元)
1.1 逻辑地址和线性地址
逻辑地址
- 编译器编译程序时,会为程序生成代码段和数据段,指令或者数据相对于段起始地址的偏移称为逻辑地址。
线性地址
- CPU加载程序后,会为这个程序分配内存,所分配内存又分为代码段内存和数据段内存。代码段内存的基址保存在CS中,数据段内存的基址保存在DS中。
两者的关系
- 段基址+逻辑地址 = 线性地址
x86的CPU实模式下,段基址表示的是内存物理地址。而保护模式下段基地存放在描述符中,通过选择子加上逻辑地址可以访问对应的指令或数据,而在ARM处理器中没有这两个概念,他们都被称为虚拟地址。
1.2 虚拟地址转换成物理地址
ARM虚拟地址的结构
- TTBRx(页表基地址寄存器),里面存放了一级页表的首地址
- 一级页表里面有4096个页表项(每一个表项里面存储了二级页表的位置,通常也叫做页目录)
- 通过L1索引可以访问一级页表查找到二级页表的首地址
- 通过L2索引可以访问二级页表中页起始地址(二级页表项高20位和虚拟地址的低12位组合就是对应的物理地址)
1.3 用户空间和内核空间
Linux系统将程序划分为内核空间和用户空间,内核空间特权级更高。
- 通常将4G的虚拟地址空间中的0-3GB作为用户空间,3-4GB作为内核空间。
用户申请内存的方式
- 用户空间调用malloc函数时,使用brk系统调用申请内存,在内核中sys_brk用于处理该系统调用。
- 用户空间使用的内存是虚拟内存,在堆上申请的内存也是虚拟内存,使用vma管理,包含vma的创建,插入,删除等。
- 创建vma之后返回对应的虚拟地址,但是无法往里面写入数据,因为此时访问的虚拟地址并不存在,因此会产生一个异常叫做缺页中断。
- 缺页中断用于建立虚拟地址到物理地址的映射,分配对应的物理内存,并将物理地址写入到页表中。
二、内核中的内存管理模块
匿名页面和 page cache
- 物理页面可以分为匿名页面 (没有关联任何文件的页面,例如malloc) 和 page cache(关联了具体文件的缓存页面)。
2.1 页面分配器
- 匿名页面和 page cache的产生依赖于页面分配器,分配的基本单位为一个页框。
- 页面分配器将页面分配好之后会进行页表管理(包括内核页表和进程页表)。
2.2 SLAB
- 当用户需要分配的空间只有几个字节的时候,会使用SLAB来进行内存分配。
- SLAB可以管理特定大小的对象缓存,可以快速的进行分配和回收,而不必每次都需要页面分配器。
2.3 页面回收
- 当系统内存短缺的时候,将会把不常用的内存页换出到交换区中(page cache 或者匿名页面)。
- 系统中有一个守护进程,当系统可用内存低于某个警戒线的时候,它将会从LRU链表中查找不常用的内存并回收(系统更加倾向page cache,如果page cache里面是干净的会直接回收,如果是脏的,需要放到交换区中,匿名页表中存储的是进程私有数据,需要放到交换区)。
- 反向映射机制(reverse map),它可以找到所有映射到某个页面的虚拟地址空间,为页面回收机制而服务,从而断开虚拟地址与物理页面的映射。
2.4 KSM
KSM(Kernel Samepage Merging)用于将两个完全相同的匿名页面进行合并,当进行写操作时候再将其分开,也就是写时复制。
2.5 Huge Page
用于分配较大的内存,通常大于2M就可以使用它,在服务器中比较常见。
2.6 页迁移
内存中的部分页面可以进行迁移(在页面规整和内存热插拔用到了该技术)。
2.7 内存规整
缓解内存碎片化
2.8 OOM
OOM用于去查找内存较多的进程结束运行