内存管理是Linux内核最复杂的组件。内存管理包括虚拟内存机制和物理内存管理。这篇说说物理内存管理的一些要点。
物理内存地址空间和虚拟内存地址空间
说到虚拟内存的时候我们知道虚拟内存地址空间分为两部分:内核地址空间和用户进程地址空间。这两个地址空间都使用虚拟地址,也就是说程序使用的都是虚拟地址。从虚拟地址映射到实际物理地址时有所区别:
1. 内核使用物理内存时可以直接通过虚拟地址和内核地址空间的起始值的偏移量来计算得到实际物理内存的地址
2. 进程使用物理内存必须通过页表结构进行转换
对应内核地址空间来说,
1. 如果内核虚拟地址空间 > 物理内存地址空间时, 那么物理内存地址空间可以全部映射到内核虚拟地址空间。在目前64位机器的情况下基本都是这种情况,由于硬件的限制,可用的物理内存远小于可用的内核虚拟地址空间
2. 如果内核虚拟地址空间 < 物理内存地址空间时,物理内存地址空间的一部分映射到内核虚拟地址空间,剩余的物理内存地址空间被称为高端内存(high memory),内核会采取额外的映射机制来访问这些高端内存。这种情况在之前的32位机器上是常态,在32位机器下,内核地址空间和用户进程地址空间的比例默认为1:3,也就是说4G的虚拟地址空间,内核地址空间占1G,而可用的物理内存可达到4G,内核地址空间还必须预留一部分作为内核运行使用,所以可用的内核地址空间对物理内存地址空间的映射为896MB,剩余的物理内存地址空间都是高端内存
对于64位机器来说,虚拟内存地址空间远大于可用的物理内存地址空间,所以虚拟内存地址空间划分成内核地址空间和用户进程地址空间时就比32位机器的地址空间富裕很多,整个64位虚拟地址空间分为上下半部和中间禁用区三部分,可以看到虚拟地址的第0到46位都是任意设置的,而47位到63位对于内核地址空间来说都是0,对于用户进程地址空间都是1。这种虚拟地址称为规范的地址,其他的都是不规范的地址。所以在64位机器来说内核虚拟地址空间和用户进程虚拟地址空间都是2的47次。
下图是更细节的描述,内核的虚拟内存地址空间的起始部分是对物理内次地址空间的一致性映射,然后是vmalloc区域,然后是给内核使用的其他内存区域
之前说虚拟内存的时候已经说了页表的结构,再来看一下页表和一个虚拟地址的映射关系
1. 对于64位机器,4KB的页大小来说,Offset表示该地址在页内的偏移量,所以Offset 的长度=12, 2的12次方正好是4KB
2. PGD,PUD,PMD,PTE各位的长度都是9,也就是每级页表的页表项都是512个
3. 这样总共64位的虚拟地址长度使用了48位,已经远大于目前可用的物理内存地址空间
而页表结构如下,每级页表实际就是一个一个的数组,数组项的长度是64位的,上级页表的页表项存放着下级页表的物理内存地址,而最后的PTE页表项存放的就是实际物理内存页的页号。有了物理内存页的页号我们就可以找到对应物理内存页。物理内存地址空间就是按照物理页长度划分的