《汇编程序设计与计算机体系结构》第十章学习笔记
作者:互联网
系统寄存器
系统寄存器有下面三组 :
控制寄存器
内存管理寄存器
特定于机器的寄存器
控制寄存器 : 用来表示处理器的模式以及与当前正在执行的任务有关的一些特征, 主要有 : cr0 - cr4及cr8, 其中cr8寄存器为任务优先级寄存器, 用来安排外部中断的优先顺序, 该寄存器只能在64位模式下使用
内存管理寄存器(重要) : 用来指出保护模式的描述符表所在的位置, 主要有 :
gdtr : 全局描述符寄存器 (Global Descriptor Table Register)
ldtr : 局部描述符表寄存器 (Local DescriptorTable Register)
idtr : 中断描述符表寄存器 (Interrupt Descriptor Table Register)
tr : 任务寄存器 (Task Register)
特定于机器的寄存器 : 用来控制并报告处理器的执行情况.
段寄存器
段寄存器是一种 16 位宽的寄存器. 代码段, 数据段, 栈段以及附加段都有其对应的段寄存器, 分别称为 cs, ds, ss, es. 此外还有两个通用的段寄存器称作 fs 与 gs.
es 是执行 movs, cmps 及 scas 等字符串操作时默认使用的段寄存器
fs 与 gs 的用途没有明确为硬件所定义.
处理器模式
主要为 实模式(8086处理器), 保护模式(主要为80386处理器), 长模式(主要为AMD处理器)
这三者的最主要的区别在于可寻址的最大物理内存空间不同, 以及寻址方式的不同
模式 | 实模式 | 保护模式 | 长模式 |
---|---|---|---|
第一个支持该模式的处理器 | Intel 8086 | Intel 80386 | AMD Opteron |
可寻址的物理内存空间 | \(2^{20}\) (1MB) | $2^{32} $ (4GB) | 实际上 : \(2^{48}\)(256TB) 理论上 : \(2^{64}\)(16EB) |
实际上还有 Intel 80286 处理器, 使用的模式是保护模式, 可寻址的空间为 \(2^{24}\)(16MB), 不过由于该处理器并不是很完善, 因此不久之后就被更加优秀的 Intel 80386 处理器所替代.
Intel 8086 : 是一款支持 x86 指令集的 16 位处理器, 它运行在,实模式下, 这意味着软件能直接访问物理内存空间, I/O 通道及周边软件. 在现在看来这种模式存在一定的安全隐患, 但是计算机在开机时需要软件来直接与硬件沟通实现操作系统的启动, 因此实模式直到现在仍然被保留.
Intel 80386 : 在 Intel 8086 和 Intel 80286 处理器上升级改进而来. 目前绝大多数的 32 位操作系统都是建立在 Intel 80386 或其改进型的处理器之上. 从 Intel80286 处理器开始加入保护模式, 这种模式与原来的实模式相比扩大了可寻址的物理内存空间, 且大部分软件都无法直接访问物理内存空间, 寻址方式中加入了一些抽象机制, 例如描述符和分页等等. 32 位处理器向下兼容 16 位处理器
AMD Operon : 使用长模式, 从而可以使用 x86_64 指令集中的指令以及与之相关的寄存器. 该模式必须搭配 64 位操作系统. 长模式下有几种子模式, 其中 64 位模式适用于 64 位操作系统, 而兼容模式则适用于 32 位以及 16 位程序. 在不使用长模式的情况下, x86_64 处理器是可以支持实模式和保护模式的
内存模型
计算机系统所用的内存模型要根据可使用的处理器模式以及操作系统的支持情况来决定. 常见的两种模型为 : 分段模型与平面模型
分段模型
分段内存模型会把地址空间分成不同的区段或者是段落, 以供程序加中的各个部分使用. 这种模型通过段基址加偏移量的方式来指定内存地址
平面模型
平面模型会把整个内存空间展示成一块连续的区域以供开发者及程序来使用. 此外, 该模型也叫做线性模型, 所有的内存地址都是直接以它在线性空间中的位置来确定的, 而不通过段与段中的偏移量决定
两种模型都是将逻辑地址映射成物理地址,
对于 x86 来说, 16 位系统使用分段内存模型, 实模式下的 32 位系统也是如此, 保护模式下实际使用的同样是分段模型, 不过看上去和平面模型一样. x86_64 使用平面模型
实模式和保护模式下寻址的区别
实模式下的寻址
假设在一个 16 位的环境下, 数据段从 1234h 开始, 我们要找的数据与该段开头的距离是 5678h. 这份数据所在的地址需要按照段选择器与偏移量的形式来书写, 也就是要写成 1234h : 5678h.
逻辑地址 | 1234h : 5678h |
---|---|
第一步 | 1234h * 16 = 12340h |
第二步 | 12340h + 5678h = 179B8h |
关于第一步为什么要 * 16, 因为在实模式下的地址空间是 \(2^{20}\), 这意味着物理地址应该用 20 个进制位去表示. 而段选择器只有 16 个二进制位, 所以要左移 4 位来达到 20 个进制位.
在 x86 地址中的段选择器与偏移量相加的这种形式一般都写成 ds:[xxxx], ss:[xxxx]等. 实模式下的寻址较为简单
实模式下的寻址 : 20位的段选择器 + 偏移量
保护模式下的寻址
32 位系统在保护模式下的寻址较实模式下寻址要复杂一些, 虽然都使用的是分段模型,但是保护模式中引入了一些抽象机制. 其中一个是描述符, 另一个是分页.
计算机在启动时, 处理器会实现先从实模式开始运行, 然后很快转换到保护模式下运行的过程. 整个启动的过程中系统会设定描述符, 主要有
中断描述符表(IDT, Interrupt Descriptor Table)
全局描述符表(GDT, Global Descriptor Table)
局部描述符表(LDT, Local Descriptor Table)
任务状态段描述符(TSS, Task State Segment)
代码段描述符(CSD, Segment Descriptor)
数据段描述符(DSD, Data Segment Descriptor)
其中, GDT 与 LDT 是用来详细描述内存段的数据结构, 可以指出内存段的基址, 大小与访问权限
GDT 针对的是全局及共享区段, 而每个运行中的程序 / 进程则由各自的 LDT 可以使用.
TSS 用来存放任务所涉及的寄存器值, I/O 权限及栈指针, 以便支持多任务处理, 尤其是暂停及切换任务
描述符寻址
下面来说明保护模式下如何使用描述符寻址, 同样还是上面那个例子 (1234h : 5678h). 由于段选择器并不再是直接指向某个内存段, 而是用索引来查询 GDT 表. 32 位环境下的 GDT 表每个条目间的距离均为 8 字节, 64位环境向下是 16 字节.
逻辑地址 : 1234h : 5678h
第一步 | 1234h (在GDT中查询) = 90120h |
---|---|
第二步 | 90120h + 05678h = 95798h |
GDT表 (TI = 0)
索引 | 索引所对应的基址 |
---|---|
00h | NULL |
08h | 基址 |
... | 基址 |
1234h | 基址 = 90120h |
在 32 位保护模式下, 段基址与偏移量均为 32 位. 和实模式下一样, 段选择器这部分也保存在某个段寄存器中. 下面这张表格是 1234h 通过索引取基址的过程
二进制位 | 15 - 3 | 2 | 1 - 0 |
---|---|---|---|
内容 | 索引 | 表格指示符 (TI) | 请求优先级 (RPL, 请求权限级别) |
RPL : 值可以是0 \((00)_b\) 或 3 \((11)_b\), 分别表示内核级别与用户级别.
TI : 如果是 0 在 GDT 中查找, 为 1 在 LDT 中查找
索引 : 查表的依据
在 32 位环境下, ds, ss 及 es 寄存器一般都指向同一个位置, 这是因为系统将这三者所指向的区段全都认定成用来保护数据的区段.
fs 和 gs 是给开发者使用的, 通常默认为 0x0h, 用来表示整个地址空间的起始处, 它们的取值也可以与 ds 相同.
cs 是代码段的段选择器, 该段用来保存当前正要执行的指令
控制寄存器
在讲分页寻址之前, 先来讲讲 cr0 - cr4 这 4 个控制寄存器.
cr0 寄存器主要关注三个二进制位, 分别是 : PE, PG, WP
PE : 保护(Protection Enable)标志. 当该位置 1 时即开启了保护模式; 当置 0 时即进入实模式。
PG : 分页(Paging)标志。当该位置 1 时即开启了分页机制; 当置0时则禁止分页机制,此时所有线性地址等同于物理地址.
(若要启用分页机制,那么PE和PG标志都要置 1 .)
WP : 写保护(Write Proctect)标志. 当该位置 1 时, 处理器会禁止超级用户程序(例如特权级0的程序)向用户级只读页面执行写操作; 置 1 时, 则反之.
cr1 寄存器保留不用
cr2 寄存器用于出现页异常时报告出错信息。在报告页异常时,处理器会把引起异常的线性地址存放在 cr2 中。
cr3 寄存器含有页目录, 页表和页面的物理内存基地址,因此该寄存器也被称为页目录基地址寄存器 PDBR ( Page-Directory Base addressRegister ).
cr4 寄存器中存储了CPU工作相关以及当前人任务的一些信息
我们重点关注 cr0 与 cr3
分页寻址
分页机制是 x86 系统的处理器在保护模式下新加入的一种抽象机制, 当前的各种操作系统都在使用分页机制. 要想使用分页的话 cr0 的 PG 位和 PE 位必须置 1 . 启用分页后系统要通过 cr3 寄存器把线性虚拟地址转换成物理地址, 下面这张表格展示了 x86 分页机制的结构安排以及 32 位虚拟地址的解读方式
32 位的 x86 使用多级分页机制.
页面 : 页面大小为 4 KB, 在系统中以连续内存空间的形式出现. 如果开启了 PSE ( Page Size Extension, 页面大小扩展), 即 cr4 的 4 号位 ( 见 cr4 图) 则为 2 MB 或 4 MB.
页表 : 一个含有 1024 个元素的数组, 每个数组元素的大小为 4 个字节, 这时会发现刚好可以放入内存页面中. 页表中的每个元素都指向某个页面所在的物理地址
页目录 : 一个含有 1024 个元素的数组, 每个数组元素的大小为 4 个字节. 页目录中的每个元素都指向某个页表所在的物理地址
现在可以来讲讲 32 位下的 x86 如何使用分页机制来寻址了. 系统在获得某个虚拟地址后, 按下列方式来确定数据的物理地址 :
首先, 根据 cr3 找到当前页目录的基址. 然后将虚拟地址的高 10 位 ( 也就是 22 至 31 号位 ) 当成索引, 在页目录中查找与该索引相对应的元素, 并根据该元素的取值找到对应页表所在的基址
接下来, 把虚拟地址的中间 10 位 ( 也就是 12 至 21 号位) 当成索引, 在页表中查找与该索引相对应的元素, 并根据该元素的取值找到对应的内存页面所在的基址
最后, 把虚拟地址的低 12 位 ( 也就是 0 至 11 号位 ) 当成索引, 在页面中查找与该索引相对应的元素, 这个元素就是虚拟地址所指代的那个数据.
这种 32 位的虚拟地址使得 4GB 的内存空间当成一个平面模型来用, 从而令程序数据能够以页面文件的形式出现在该空间中. 系统会按照相应的方式解读虚拟地址, 并结合 cr3 寄存器的内容确定其物理地址. 页面文件保存在辅助存储器中, 并按需移动到内存或缓存中. 如果要找的数据不在内存或缓存中, 则会出现页缺失
平面模型下的寻址
64 位的 x86_64 架构在寻址的时候几乎完全抛弃了复杂的分段机制. 其中 cs、ds、ss、与 es 寄存器要么强行设为 0x0h, 要么当成 0 来对待.同时可寻址空间也由 \(2^{32}\) 上升到 \(2^{64}\), 这使得代码、数据与栈都可以出现在一个平面的地址空间中.
x86_64 分页机制在原有的三层结构上又加了一层, 此时 cr3 不直接指向页目录, 而是指向页目录指针表, 系统会通过该表中的元素 (或者说指针) 来确定对应的页目录所在的位置.
补充
关于段寄存器与段选择器
段选择器可以看成是段寄存器的一个统称
为什么要使用保护模式
实模式下用户编写的软件能直接操作硬件, 有安全隐患, 这是最主要的原因
为什么保护模式同样也用的是分段模型, 为什么要加入描述符表和分页等抽象机制
在 32 位系统下, 实模式那种 段基地址 + 偏移量 的方式寻址能力过于有限且具有安全隐患, 因此我们要进行规范化. 通过上面在保护模式下使用描述符寻址的简单讲解下可以看到, 此时的段选择器中的地址不再是其本身的段基地址, 而是一个相对于描述符表首地址的偏移位置 ( 即索引 ), 此外并对其访问权限和范围进行了约束, 保证了安全性. 而描述符表的首地址在哪里呢 ? 这就要看 gdtr ( 全局描述符表 ) 或 ldtr ( 局部描述符表 ) 了. 其中的细节过于复杂, 这也是我查阅了一些资料和博客后得到结论, 可能有些不正确的地方还需斟酌.
分页机制存在的最大目的在于允许同时运行的程序使用的总内存远大于计算机的物理内存.其原理就是将程序不活跃的部分保留在磁盘中, 活跃的部分保留在内存中.
标签:保护模式,第十章,汇编程序,模式,描述符,寻址,处理器,寄存器,体系结构 来源: https://www.cnblogs.com/ICeVe/p/14726083.html