x86-32
在32位时代,x86 的 operating mode 有3种:
- 实模式(Real Mode)
- 保护模式(Protected Mode)
- 虚拟8086模式(Virtual 8086 Mode)
实模式(Real Mode)
8086的CPU是16位的,为其可以索引更大的内存地址空间,内核采用了分段机制(shift-and-add segmentation)
- 即一个逻辑地址由 segment 加上 offset 组成
基于分段机制(shift-and-add segmentation)的寻址过程:
- 获取对应的段寄存器
- 通过公式
linear address = segment << 4 + offset
计算逻辑地址
保护模式(Protected Mode)
80286 的CPU是32位的(寻址范围为4G),它也使用分段机制(table-based segmentation)
- 但它的段寄存器 segment register 存的不再是 segment 的起始地址,而是一个段选择子 segment selector
- 通过这个 segment selector 查找全局标识符表GDT表获得段描述符 segment descriptor
- segment descriptor 存的才是 segment 的起始地址
段寄存器 segment register 中存放的就是16位的数据结构-段选择子
段选择子 segment selector 的结构如下:
- Index:CPU 将索引号乘8再加上GDT或者LDT的基地址,就可以找到目标段描述符
- TL:其值为“0”查找 GDT 表,其值为“1”查找 LDT 表
- RPL:请求特权级别(可以用于判断当前代码是否处于内核态)
在保护模式下,对一个段的描述则包括3方面因素:[Base Address, Limit, Access],它们加在一起被放在一个64-bit长的数据结构中,被称为段描述符
段描述符 segment descriptor 的结构如下:
- G - Granularity 粒度(byte 或者 page),因为段的最大长度 limit 占20位
- 如果粒度为 byte,则该 segment 的寻址范围是 1MB
- 如果粒度为 page-4KB,则该 segment 的寻址范围是 4GB
- 一个 segment 的 size 是由 limit 和G位共同确定的
- D/B - Default Size/Bound
- 为“1”表示在32位模式下运行
- 为“0”在16位模式下运行
- L - 仅在64位系统中有效
- 为“1”表示在64位长模式下运行(64-Bit Mode)
- 为“0”表示在64位兼容模式下运行(Compatibility Mode)
- AVL - Available for software,留给软件用的,但在 linux 里是被忽略的
- P - Present,用于指明表项对地址转换是否有效
- P = 1:表示有效
- P = 0:表示无效
- 在页转换过程中,如果说涉及的页目录或页表的表项无效,则会导致一个异常
- DPL - Descriptor Privledge Level,表示可以访问 segment 的最低级别
- x86处理器的特权级别从
ring 0
到ring 3
,数字越小,级别越高 - 通常用户空间运行于
ring3
,内核空间运行于ring0
- 假设 DPL 为“1”,则只有当前特权级别为“0”或者“1”时,才可以访问该 decriptor 指向的 segment
- x86处理器的特权级别从
- Type - 目标段拥有的权限
- 对于 task segment 是没有意义的
- 对于 code segment 和 data segment 主要是关于 Writable,Executable 的属性
- Base Address - 基地址
基于分段机制(table-based segmentation)的寻址过程:
- 内核提供了一个寄存器GDTR用来存放GDT的入口地址
- 通过段选择子 segment selector 找到对应 GDT 条目-段描述符 segment descriptor
- 通过段描述符 segment descriptor 找到基地址
GDT&LDT 的使用条件:
- GDT 能完成被多个任务共享的内存区
- LDT 通常情况下是与任务的数量保持对等(LDT放在GDT中)
虚拟8086模式(Virtual 8086 Mode)
利用一种硬件虚拟化技术,在i386的芯片上模拟出多个8086芯片
当处理器进入保护模式后,基于实模式的应用就不能直接运行了,采用虚拟8086模式,则可以让这些实模式的应用运行在基于保护模式的操作系统上,因此这种模式也被称为 Virtual Real Mode
x86-64
进入64位的x64处理器时代后(64位CPU的寻址已经不会受到限制,可以不使用分段机制),产生了一种新的运行模式,叫 Long Mode,传统的三种模式则被统称为传统模式(Legacy Mode)
Long Mode 又分为2种子模式:
- 64位长模式(64-Bit Mode):应用程序必须也得是64位的
- 64位兼容模式(Compatibility Mode):32位应用程序也可以运行
置位 EFER 寄存器的 LME 位可以开启 Long Mode
- 由于 Long Mode 要求 paging 必须开启,所以在进入 Long Mode 之前,还需要置位CR0寄存器的PG位
- 置位 code segment 的L位可在 64-Bit Mode 和 Compatibility Mode 之间切换
四级分页机制
如果不开启分页机制,那么线性地址就等同于物理地址,这要求物理地址必须是连续的
前面我们提到 Linux 内核仅使用了较少的分段机制,但是却对分页机制的依赖性很强,其使用一种适合32位和64位结构的通用分页模型,该模型使用四级分页机制:
- 页全局目录(Page Global Directory)
- 页上级目录(Page Upper Directory)
- 页中间目录(Page Middle Directory)
- 页表(Page Table)
因此线性地址因此被分成五个部分,通过各个部分索引到对应的表,而每一部分的大小与具体的计算机体系结构有关
相关结构类型:
- Linux 分别采用
pgd_t pmd_t pud_t pte_t
四种数据结构来表示页全局目录项、页上级目录项、页中间目录项和页表项(这四种数据结构本质上都是无符号长整型 unsigned long)
PAGE - 页表 (Page Table):
字段 | 描述 |
---|---|
PAGE_SHIFT | 指定Offset字段的位数 |
PAGE_SIZE | 页的大小 |
PAGE_MASK | 用以屏蔽Offset字段的所有位 |
PMD - 页目录 (Page Middle Directory):
字段 | 描述 |
---|---|
PMD_SHIFT | 指定线性地址的Offset和Table字段的总位数,换句话说,是页中间目录项可以映射的区域大小的对数 |
PMD_SIZE | 用于计算由页中间目录的一个单独表项所映射的区域大小,也就是一个页表的大小 |
PMD_MASK | 用于屏蔽Offset字段与Table字段的所有位 |
PUD_SHIFT - 页上级目录 (Page Upper Directory):
字段 | 描述 |
---|---|
PUD_SHIFT | 确定页上级目录项能映射的区域大小的位数 |
PUD_SIZE | 用于计算页全局目录中的一个单独表项所能映射的区域大小 |
PUD_MASK | 用于屏蔽Offset字段,Table字段,Middle Air字段和Upper Air字段的所有位 |
PGDIR_SHIFT - 页全局目录 (Page Global Directory):
字段 | 描述 |
---|---|
PGDIR_SHIFT | 确定页全局页目录项能映射的区域大小的位数 |
PGDIR_SIZE | 用于计算页全局目录中一个单独表项所能映射区域的大小 |
PGDIR_MASK | 用于屏蔽Offset, Table,Middle Air及Upper Air的所有位 |
基于分页机制(paging)的寻址过程:
- 从CR3寄存器中读取页目录所在物理页面的基址(即所谓的页目录基址),从线性地址的第一部分获取页目录项的索引,两者相加得到页目录项的物理地址
- 第一次读取内存得到 pgd_t 结构的目录项,从中取出物理页基址取出(具体位数与平台相关,如果是32系统,则为20位),即页上级页目录的物理基地址
- 从线性地址的第二部分中取出页上级目录项的索引,与页上级目录基地址相加得到页上级目录项的物理地址
- 第二次读取内存得到 pud_t 结构的目录项,从中取出页中间目录的物理基地址
- 从线性地址的第三部分中取出页中间目录项的索引,与页中间目录基址相加得到页中间目录项的物理地址
- 第三次读取内存得到 pmd_t 结构的目录项,从中取出页表的物理基地址
- 从线性地址的第四部分中取出页表项的索引,与页表基址相加得到页表项的物理地址
- 第四次读取内存得到 pte_t 结构的目录项,从中取出物理页的基地址
- 从线性地址的第五部分中取出物理页内偏移量,与物理页基址相加得到最终的物理地址
- 第五次读取内存得到最终要访问的数据
- 程序的线性地址将作为各级页表索引
- 因此内核会给用户程序提供一个抽象:虚拟地址
- 虚拟地址到线性地址的转换则依靠分段机制
其他差异
在 Legay Mode 中,用户空间可通过 SYSENTER 指令进入内核空间,内核空间则通过 SYSEXIT 指令返回用户空间,在此过程中,由于发生了 segment 切换,所以需要进行 segmentation 的各种检测,比较影响效率
在 Long Mode 中,伴随着 segmentation 的弱化和 flat momery model(平坦内存模型)的使用,SYSENTER/SYSEXIT 这2个指令不再被支持,取而代之的不需要 segmentation 检测的 SYSCALL 和 SYSRET 指令
此外,Legacy Mode 还提供了一种叫 task-state segment (TSS) 的硬件机制,可以在发生 task switch 时,自动保存 task 的状态信息,可理解为硬件辅助的进程切换,由于主流操作系统很少用到这一机制,在 Long Mode 中已经不再支持 TSS