翻阅《深入理解linux内核》之内存寻址章节,写此文章,作为总结。
X86架构,关于地址类型有3个重要概念:
逻辑地址(logical address)
每个逻辑地址由一个段地址和偏移量组成,偏移量指实际地址相对于段首的偏移。即logical addr = segment addr + offset
线性地址(linear address)
也称虚拟地址,无符号整数,值范围是0x00000000~0xFFFFFFFF
物理地址(pyhsical address)
物理内存单元的地址,通过电信号能访问到的地址
后续相关重点涉及线性地址和物理地址。
硬件中的分页
CPU芯片有一个内存控制单元(MMU),其一重要的功能是将线性地址转换为物理地址。
首先介绍涉及到得重要概念:
1. 页:为了效率起见,线性地址被分为以固定长度为单位的组,称为页。页连续的线性地址映射到连续的物理地址中;
2. 页框:分页单元把所有的RAM分成固定长度的页框(page frame),也可称物理页;页框的长度等于页的长度;
3. 页表:线性地址映射到物理地址的数据结构称为页表(page table);
据书中记载,常规分页,从80386起,Intel处理器的分页单元处理4KB的页,那页的大小是怎么计算出来的呢?
X86架构处理器32位线性地址划分为3个域:
域名 | Bit Size | space size |
Directory(目录) | 21~31 | 1024K目录项 |
Table(页表) | 12~21 | 1024K页表项 |
offset | 0~11 | 4K |
每个进程必须独占一个分配给他的页目录,这样每个进程可以有相同的地址空间。但不必立刻为进程的所有页表分配RAM,当进程实际需要一个页时才分配RAM。
正在使用的页目录的物理地址存在在控制寄存器CR3中, 通过解析线性地址->Directory字段找到页目录中的目录项->找到与目录项与之对应的页表->Table字段找到页表中的表项->找到与表项对应的页框的物理地址->offset字段找到页框中某字节的物理地址。
硬件分页一个重要的环节是根据表示地址空间大小,将线性地址分为多个域,在域之间进行逐级查找,最后找到对应的物理地址。这点从常规分页线性地址到物理地址的转换可以看出。书中还涉及扩展分页,物理地址扩展分页机制(PAE),64位系统分页,不再详述,原理都是一样的,只是线性地址每个域的划分方法不同。
Linux中的分页
内核2.6.11版本开始,采用四级分级模型:
页全局目录(page global directory)
页上级目录(page upper directory)
页中级目录 (page middle directory)
页表(page table)
每一部分的大小与具体的计算机体系架构有关。X86和ARM是怎么划分的呢?
没有启动PAE的32位系统,页上级目录和页中级目录位置为0,但页上级目录和页中级目录在指针序列中的位置被保留; 启动了PAE的32位系统,取消了页上级目录,使用3级目录结构; 64位系统,根据硬件对位的划分,决定使用三级结构(取消页上级目录)或者四级目录结构
Linux中页的权限访问
页与页表特权级别有2个,由user/Supervisor标志所控制;页得存取权限位有两种:读与写
进程页表地址空间
进程的线性地址空间分为两部分:
0x00000000~0xBFFFFFFF的线性地址,进程空间和内核空间都可以访问;
0xC0000000~0xFFFFFFFF的线性地址,只有内核空间可以访问;
进程运行于用户态时,它产生的地址空间小于0xc0000000, 当进程运行于内核态时,它产生的地址空间大于0xc0000000。即是0~3G为用户空间,3G~4G为内核空间。