我们知道linux采用了分页机制,通常采用四级页表,页全局目录(PGD),页上级目录(PUD),页中间目录(PMD),页表(PTE)。如下:
- 从CR3寄存器中读取页目录所在物理页面的基址(即所谓的页目录基址),从线性地址的第一部分获取页目录项的索引,两者相加得到页目录项的物理地址。
- 第一次读取内存得到pgd_t结构的目录项,从中取出物理页基址取出,即页上级页目录的物理基地址。
- 从线性地址的第二部分中取出页上级目录项的索引,与页上级目录基地址相加得到页上级目录项的物理地址。
- 第二次读取内存得到pud_t结构的目录项,从中取出页中间目录的物理基地址。
- 从线性地址的第三部分中取出页中间目录项的索引,与页中间目录基址相加得到页中间目录项的物理地址。
- 第三次读取内存得到pmd_t结构的目录项,从中取出页表的物理基地址。
- 从线性地址的第四部分中取出页表项的索引,与页表基址相加得到页表项的物理地址。
- 第四次读取内存得到pte_t结构的目录项,从中取出物理页的基地址。
- 从线性地址的第五部分中取出物理页内偏移量,与物理页基址相加得到最终的物理地址。
- 第五次读取内存得到最终要访问的数据。
整个过程是比较机械的,每次转换先获取物理页基地址,再从线性地址中获取索引,合成物理地址后再访问内存。不管是页表还是要访问的数据都是以页为单位存放在主存中的,因此每次访问内存时都要先获得基址,再通过索引(或偏移)在页内访问数据,因此可以将线性地址看作是若干个索引的集合。
虚拟内存管理
Linux通过红黑树管理
1 | struct task_struct { |
该结构体是一段虚拟内存,从vm_start到vm_end,他们拥有相同的权限;
vm_prev 、vm_next 是双向链表的next与pre;
vm_rb 是红黑树节点
1 | struct mm_struct { |
mmap是双向链表;按照地址大小来顺序管理所有的area
mm_rb是红黑树的根节点
红黑树的Key-Value: 虚拟地址 => 对应的area.