Board logo

标题: 80X86保护模式 [打印本页]

作者: 苹果也疯狂    时间: 2015-7-20 08:46     标题: 80X86保护模式

本文是想简单的描述80x86 CPU在保护模式下的工作方式。
        本文仅描述大概的东西,细节的并不多描述,比如一个描述符中的每位表示什么意思,这些不会涉及。具体可google。
        本文没有图,因此难以理解。

        1:控制寄存器(CR0~CR3)
        CPU内含有4个控制寄存器,CR0~CR3,均为32位寄存器。
        CR0:第0位为PE位,第31位称为PG位。  PE控制是否开启保护模式, PG控制是否开启分页机制。

        PE         PG             说明
        0           0               实模式
        0           1               无效组合
        1           0               保护模式
        1           1               保护模式 + 分页机制

        CR1:保留寄存器,没用
        CR2:存储页错误线性地址,即发生页异常时的线性地址保存在这里。(貌似用于缺页异常)
        CR3:页目录表基地址。

        CR2和CR3是用于分页机制的。关键的是CR0,当置位PE位时,CPU便进入了保护模式。
        进入保护模式意味着CPU的工作机制发生了变化。访问内存的方式发生了变化,中断机制发生了改变,相比之实模式还多了许多的功能。

        2:内存访问
        不管实模式还是保护模式,当CPU访问内存的时都是提供如下形式的地址:
                      段地址:偏移地址
        虽然形式一致,但意义有很大差异。这种地址就称为“虚拟地址”
        由于意义的改变,通常将保护模式下的地址称为:    段选择子:偏移地址
        无论保护模式、实模式,段选择子和段地址通常都是存放在段寄存器中的,如CS, DS, ES, FS,GS,SS

        转换成物理地址的方式:
        (1)实模式
        实模式下计算物理地址的公式:物理地址 = 段地址 * 16 + 偏移地址
        (2)保护模式
        保护模式得到物理地址的方式: 段选择子:偏移地址  ---->  分段机制  -----> 线性地址  ------> 分页机制 ------> 物理地址
        要想了解保护模式是如何访问物理地址的,则需要清楚分段机制和分页机制。

        3:分段机制
        首先要了解,当开启保护模式后,就开启了分段机制,但仅有置位PG位后,才会开启分页机制。因此要不要分页,由操作系统决定。
        分段机制用到的数据结构为:描述符表,系统寄存器

        (1)描述符表
        描述符表就是由描述符组成的表。
        每个描述符的大小为8字节,描述符表就是在内存中的一个数组,数组元素为大小8字节的描述符。
        根据描述符的内容不同,可将描述符分成两大类:段描述符和门描述符。

        段描述符内容包含:
        段基地址     段限长        段属性
        段描述符分为:代码段描述符,数据段描述符,系统段描述符。
        其中系统段描述符分为:LDT段描述符, TSS段描述符

        门描述符内容包含:
        段选择子     段内偏移     段属性
        门描述符分为:中断门描述符,陷阱门描述符,任务门描述符,调用门描述符。

        描述符的意义:
        一个段描述符指定了一个段。 也就是说当我们谈到“段”的时候,我们谈到的是一段虚拟内存空间。
        段基地址指明了这段空间的起始地址,段限长指定了段的大小,而段属性就说明了段的属性,比如是代码段还是数据段,该段的访问权限等等信息。
        关于门描述符的意义,下面再说。

        系统有三种描述符表:
        全局描述符表(GDT):全局的
        局部描述符表(LDT):用于任务
        中断描述符表(IDT):用于中断处理

        (2)寄存器
        全局描述符表寄存器(GDTR):存储GDT表的线性基地址(32位)和表长度(16位)
        中断描述符表寄存器(IDTR):存储IDT表的线性基地址(32位)和表长度(16位)
        局部描述符表寄存器(LDTR):存储当前使用的LDT表的段选择子(16位)
        任务寄存器(TR):存储当前任务的段选择子(16位)

        注意:这些寄存器里存储的是线性地址,不是物理地址。

        (3)段选择子
        在保护模式下提供的地址里,前一部分称为段选择子,
        16位的段选择子分成3个部分:描述符索引(13位)     TI位(1位)     RPL(2位)
        明显描述符索引是用来选择描述符的。
        当TI位为0时,就会在GDT表中访问描述符索引指定的描述符
        当TI位为1时,就会在LDT表中访问描述符索引指定的描述符

        (4)地址转换
        保护模式下,分段机制的地址转换过程大致如下:(抱歉没有图,只有文字说明)
        i) CPU访问地址为“段选择子:偏移地址”
        ii)利用“段选择子”部分的值和相应的寄存器GDTR/LDTR,获得相应描述符表中的描述符。
        iii)进行相应的访问权限检查
        iv)通过检查后,将描述符中的“段基地址”与“偏移地址”值相加得到“线性地址”。(如果没有开启分页机制,则线性地址就是物理地址)

        与实模式相比, 保护模式里多了一步查表操作。

        (5)有关LDT
        LDT描述符表是作为一个段而存在的,因此需要有一个LDT段描述符来描述LDT段。
        可以将一个任务的代码段和数据段的描述符放在一个LDT段描述符表中,然后让不同的任务有不同的LDT。
        让LDTR指向不同的LDT时,就进行了“任务的切换”。(这只是简单的说法)

        (6)需要注意的
        关于描述符表,有意思的是它存储在内存里。有关这点,我还不太明白。


        4:分页机制
        通过分段之后得到线性地址,若开启了分页机制,则通过分页机制,才能得到物理地址。

        通常一页的大小为4KB。
        分页机制将线性地址空间按4KB大小分成一些页,将物理地址空间也按同样大小分成一些页,
        通过页表这个结构,对两个地址空间中的页建立映射关系。不一定是一一映射。

        (1)页表项
        一个页表是由一些页表项组成,页表项大小为4个字节。
        页表项包含两部分内容:      页物理地址      属性
        因此通过页表项,可找到对应的物理页。

        (2)二级页表结构
        页目录存储在一个页面中,每个页目录也是由页表项组成的。页目录中的页表项指向的页的内容为页表。
        一个页面的页目录,会有1024个页表项,每个页表项指向一个页表,因此一个页表最多可以访问4M的物理地址空间。
        于是一个页目录可以访问4G的物理地址空间。

        (3)地址转换过程:
        i)32位的线性地址分成以下3个部分: 页目录索引(10位)     页表索引(10位)    页内偏移(12位)
        ii)利用CR3寄存器和页目录索引,找到页目录中的一个页表项,得到二级页表的页地址
        iii)利用页表索引和二级页表的页地址,得到要访问页的物理地址
        iv)利用页的物理地址和页内偏移,得到要访问内存单元的物理地址。


        5:门描述符
        这里说明门描述符的作用。 此处以调用门为例,其他中断门,陷阱门类似。
        现在运行在代码段A中,若想调用代码段B的代码。方法有以下两种:
        i)JMP/CALL + 代码段B的段选择子
        ii)JMP/CALL + 调用门描述符

        (1)直接使用代码段B的段选择子
        在进行代码跳转时会进行特权级检查。  在直接使用段选择子进行跳转时,系统的特权级不会发生改变,堆栈也不会改变。

        (2)使用调用门描述符
        在门描述符里包含有要跳转的代码段B的段选择子, 因此使用调用门,只是中间多了一步而已。
        使用调用门的作用是,可以从低特权级代码段,跳转到高特权级代码段。 就好像应用程序调用系统函数的时候,会从用户态进入内核态。

        (3)堆栈问题
        当特权级发生变化的时候,堆栈也会发生变化。
        每个任务会最多有4个堆栈, 对应于4个特权级, 当任务从低特权级跳转到高特权级后,任务的堆栈也会发生变化。
        这4个堆栈的指针存放在任务的TSS段中。
        CPU会将旧的堆栈中的参数拷贝到新堆栈中, 并且保存旧堆栈的SS和ESP,还有返回地址CS与EIP。
        这样在调用ret指令返回时,能返回到之前的地址,并且恢复之前的堆栈。

        6:中断机制
        系统为每个中断分配一个中断向量号,共有256个中断向量后。
        当发生中断时,会用中断向量号在IDT表中查找对应的门描述符, 然后利用门描述符中段选择子指定的代码来处理中断。

        感觉中断与异常处理起来都差不多。

        7:任务管理
        一个任务包括:任务执行空间和任务状态段TSS
        任务执行空间包括:代码段、数据段、堆栈段。

        (1)TSS段
        TSS段保存了一个任务的主要信息:
        i)通用寄存器和段寄存器信息
        ii)标志寄存器EFLAGS, 程序指针EIP
        iii)特权级0、1、2的堆栈指针
        iv)I/O映射位图
        v)LDT段选择符
        在进行任务切换时,会将旧任务的相应信息保存在TSS中, 新任务的相应信息从TSS中加载到寄存器中

        (2)执行任务
        当使用JMP/CALL调用一个TSS段选择子或者任务门描述符时,就会开始执行一个任务。执行时就会将旧的任务信息保存到TSS中。
        从新任务的TSS内容加载到CPU寄存器中。  第一个任务可以用LTR指令,将TSS段选择子加载到TR寄存器中。




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0