3.操作系统——CPU的实模式、保护模式和长模式

实模式、保护模式、长模式

实模式16(实地址模式)

真实分为两个方面:

  1. 运行真实指令。不区分指令动作,只是直接执行指令的真实功能
  2. 发往内存的地址是真实、不加限制的。

总结来说就是,这个模式下直接往物理层写,而不是我们上一章说过的硬件抽象层

实模式寄存器

X86CPU在实模式下的每个寄存器都是16位。
3.操作系统——CPU的实模式、保护模式和长模式_第1张图片
其中:

  • AX:累加器,主要用于存放数据,算数、逻辑运算中的操作数或者结果。也可用来存放临时的地址。
  • BX:基地址寄存器,存放访问存储器时的地址。
  • CX:计数寄存器,保存计算值,如在移位、循环等指令中作为隐含计数使用。
  • DX:数据寄存器:用于数据传递。在间接寻址时IO指令存放IO端口地址。
  • IP:程序指针,指向下一条指令地址
  • SP:栈指针,指向当前栈顶
  • S结尾:段寄存器存放内存段基地址

实模式下如何访问内存

数据和指令都是放在内存中,如何寻址将数据装入寄存器?
所有内存地址得来的方式都是:段寄存器左移4位,再加上一个通用寄存器中的值形成地址。由这个地址去访问内存,着就是分段内存管理模型
3.操作系统——CPU的实模式、保护模式和长模式_第2张图片

注意:
代码段:由CS(段寄存器)和IP(程序指针)确定
栈段:由SS(段寄存器)和SP(栈针)确定
由此我们知道,不同程序执行,只需要分配不同的四个寄存器CS IP SS SP,就可以分配不同运行地址。

汇编Hello world

要点:

  • 数据段和代码段开头和结尾分别以 SEGMENT 和 ENDS标识
  • 字符串单引号括住,以$结束
  • ASSUME CS:CODE,DS:DATA ;告诉汇编程序,DS指向数据段,CS指向代码段
  • 数据段data经由AX转赋给DS
  • LEA是取地址指令
  • INT指令发出:软中断,具体见下一节详解。

data SEGMENT ;定义一个数据段存放Hello World!
    hello  DB 'Hello World!$' ;注意要以$结束
data ENDS
code SEGMENT ;定义一个代码段存放程序指令
    ASSUME CS:CODE,DS:DATA ;告诉汇编程序,DS指向数据段,CS指向代码段
start:
    MOV AX,data  ;将data段首地址赋值给AX                
    MOV DS,AX    ;将AX赋值给DS,使DS指向data段
    LEA DX,hello ;使DX指向hello首地址
    MOV AH,09h   ;给AH设置参数09H,AH是AX高8位,AL是AX低8位,其它类似
    INT 21h      ;执行DOS中断输出DS指向的DX指向的字符串hello
    MOV AX,4C00h ;给AX设置参数4C00h
    INT 21h      ;调用4C00h号功能,结束程序
code ENDS
END start

实模式中断

中断是中止当前程序,立即跳转到另一个特定的地址上,以运行特定代码。
实模式下,它的实现过程:先保存CS和IP寄存器,然后装载新的CS和IP
注:IP只和CS一起使用,只能由处理器才能直接改变其值。开始一段代码的时候,CS指向代码段起始地址,IP段指向段内偏移。这样CS+IP就能形成逻辑地址,由总线接口变换成物理地址从而取得指令。处理器也会自动根据当前指令长度改变IP值,从而执行下一条指令。

中断如何产生?

  1. **硬件中断:**中断控制器给CPU发送了一个电子信号,CPU对信号作出应答,随后中断控制器将中断号码发给CPU,这是硬中断。举例:外设如键盘鼠标,或者内部时钟产生的中断。
  2. 软件中断: CPU执行了 INT指令,该指令后面跟一个常数,这个常数就是软中断号码。软中断由INT指令产生。

中断如何实现?
在内存中存放一个中断向量表,这个表由CPU特定寄存器IDTR指向。在实模式下,中断向量表条目由代码段地址和段内偏移组成。
有了中断信号,CPU根据IDTR寄存器中信息,计算出中断行向量中的条目。然后装载CS和IP(即把代码段基地址和段内偏移装入),最终响应中断。

保护模式32

寻址问题是内存不断扩大最重要的问题。假设寄存器16位,那么最多只能标识2^16个地址。
我们说了实模式下CPU对指令不区分的执行,且对访问内存的地址不加限制
鉴于上述两点,CPU还实现了保护模式。

保护模式寄存器

相比于实模式,保护模式增加了很多32位的通用寄存器,这些寄存器可以单独使用低16位甚至低8位。
3.操作系统——CPU的实模式、保护模式和长模式_第3张图片

保护模式特权级

为了区分指令(如INT OUT CLI指令)和资源(寄存器,IO,内存地址)
保护模式下CPU实现了特权级
分为四级R0_R3。R0可以执行所有指令,数字越大,级别越低,执行的指令越少。即可认为R0拥有最大权力,可访问资源也最多。R0可以访问低级别的资源,反之不行。

保护模式段描述符

16位的段寄存器肯定无法放入32位的段基址以及段内偏移。
所以把描述一个段的信息封装成了如下形式:

段描述符有64位,8B、
多个段描述符在内存中形成全局段描述符。该表的基地址和长度由CPU的GDTR寄存器指示。
3.操作系统——CPU的实模式、保护模式和长模式_第4张图片
段寄存器中不再存放基地址,而是具体段描述符的索引。
访问内存时,段寄存器中索引结合GDTR寄存器先找到内存中的段描述符,再根据这个描述符中的信息判断能否访问成功。

选择段与权限表示

在保护模式中,段寄存器中存放的并不仅仅只有描述符索引,低4位
3.操作系统——CPU的实模式、保护模式和长模式_第5张图片
影子寄存器用于缓存具体段描述符的数据,是靠硬件来操作的,对系统程序员不可见,是硬件为了减少性能损耗而设计的一个段描述符的高速缓存,不然每次内存访问都要去内存中查表,那性能损失是巨大的,影子寄存器也正好是 64 位,里面存放了 8 字节段描述符数据。

权限组成:
CS和SS中的RPL(Request Privilege Level )组成的当前权限级别(CPL),通常情况下RPL=CPL。进而 CPL 就表示发起访问者要以什么权限去访问目标段。
如果CPL大于目标段的DPL,CPU禁止访问,我们刚才说了,数字越大级别越低,权限越低。因此只有CPL

保护模式平坦模型

现代操作系统都使用分页模型,但是x86cpu并不能直接分页,而是在分段的前提下,根据需要决定是否开启分页。
保护模式的平坦模型,主要让分段变成“虚设”
因为CPU的32位的寄存器最多只能产生4GB大小的地址,所有段的段基址都设置为0,段的长度为0xFFFFF,每段4KB。这样所有段都指向同一个0-4GB的地址空间中。

保护模式中断

实模式下,中断是这样实现的:
在内存中存放一个中断向量表,这个表由CPU特定寄存器IDTR指向。在实模式下,中断向量表条目由代码段地址和段内偏移组成。
有了中断信号,CPU根据IDTR寄存器中信息,计算出中断行向量中的条目。然后装载CS和IP(即把代码段基地址和段内偏移装入),最终响应中断。(即保存当前CS和IP,运行新的CS IP。)

而在保护模式下,中断要做权限检查还要切换特权级别,所以要扩展中断向量表的信息,每个中断用中断门描述符来表示。
3.操作系统——CPU的实模式、保护模式和长模式_第6张图片
同实模式一样,保护模式中断在内存中也有中断向量表,同样是用IDTR寄存器指向。只不过中断向量表中的条目变成了中断门描述符。(实模式下中断向量表存放的是基址和偏移地址
3.操作系统——CPU的实模式、保护模式和长模式_第7张图片
产生中断的时候,CPU 首先检查中断信号是否大于最后一个中断门描述符。然后检查描述符类型,以及是否在内存中。然后检查中断门描述符的段选择子 (就是段寄存器方的东西,指示段描述符索引通过和CIDR结合生成段描述符)指向的描述符。最后做权限检查,如果CPL小于DPL并大于中断门的段选择子就指向段描述符的DPL。
如果CPL等于中断门中的段选择子指向段描述符的DPL,则为同级权限,不进行栈切换
否则进行栈切换:还要加载具体权限的SS ESP,也要筛查SS中段选择子指向的段描述符。

最后,CPU加载中断门描述符中目标代码段选择子到CS寄存器中把目标代码段偏移加载到EIP中。

切换到保护模式

x86CPU第一次加电和每次重启,都会自动进入实模式,保护模式需要程序员手动切换。
第一步,准备全局段描述符表,代码如下


GDT_START:
knull_dsc: dq 0
kcode_dsc: dq 0x00cf9e000000ffff
kdata_dsc: dq 0x00cf92000000ffff
GDT_END:
GDT_PTR:
GDTLEN  dw GDT_END-GDT_START-1
GDTBASE  dd GDT_START

第二步,加载设置 GDTR 寄存器,使之指向全局段描述符表。


lgdt [GDT_PTR]

第三步,设置 CR0 寄存器,开启保护模式。


;开启 PE
mov eax, cr0
bts eax, 0                      ; CR0.PE =1
mov cr0, eax         

第四步,进行长跳转,加载 CS 段寄存器,即段选择子。


jmp dword 0x8 :_32bits_mode ;_32bits_mode为32位代码标号即段偏移

注:为什么要进行长跳转:
因为不能直接mov数值到CS寄存器(汇编语言如此限制),所以不能直接给段寄存器赋值

接下来,CPU 发现了 CRO 寄存器第 0 位的值是 1,就会按 GDTR 的指示找到全局描述符表,然后根据索引值 8,把新的段描述符信息加载到 CS 影子寄存器,当然这里的前提是进行一系列合法的检查。到此为止,CPU 真正进入了保护模式,CPU 也有了 32 位的处理能力。

长模式(AMD64)

长模式又名 AMD64,因为这个标准是 AMD 公司最早定义的,它使 CPU 在现有的基础上有了 64 位的处理能力,既能完成 64 位的数据运算,也能寻址 64 位的地址空间。这在大型计算机上犹为重要,因为它们的物理内存通常有几百 GB。

长模式寄存器

相比于保护模式,增加了一些通用寄存器,并扩展通用寄存器的位宽,所有的通用寄存器都是 64 位,还可以单独使用低 32 位。这个低 32 位可以拆分成一个低 16 位寄存器,低 16 位又可以拆分成两个 8 位寄存器,如下表。
3.操作系统——CPU的实模式、保护模式和长模式_第8张图片

长模式段描述符

然具备保护模式绝大多数特性,如特权级和权限检查。
3.操作系统——CPU的实模式、保护模式和长模式_第9张图片
长模式下CPU不再检查段基址和段长度,而只检查DPL。

当描述符中的 L=1,D/B=0 时,就是 64 位代码段,DPL 还是 0~3 的特权级。然后有多个段描述在内存中形成一个全局段描述符表,同样由 CPU 的 GDTR 寄存器指向。
长模式下的段描述符表,加深一下理解,如下所示.


ex64_GDT:
null_dsc:  dq 0
;第一个段描述符CPU硬件规定必须为0
c64_dsc:dq 0x0020980000000000  ;64位代码段
;无效位填0
;D/B=0,L=1,AVL=0 
;P=1,DPL=0,S=1
;T=1,C=0,R=0,A=0
d64_dsc:dq 0x0000920000000000  ;64位数据段
;无效位填0
;P=1,DPL=0,S=1
;T=0,C/E=0,R/W=1,A=0
eGdtLen   equ $ - null_dsc  ;GDT长度
eGdtPtr:dw eGdtLen - 1  ;GDT界限
     dq ex64_GDT

段长度和段基址都是无效的填充为 0,CPU 不做检查。但是上面段描述符的 DPL=0,这说明需要最高权限即 CPL=0 才能访问。若是数据段的话,G、D/B、L 位都是无效的。

长模式中断

保护模式下为了实现对中断进行权限检查,实现了中断门描述符,在中断门描述符中存放了对应的段选择子和其段内偏移,还有 DPL 权限,如果权限检查通过,则用对应的段选择子和其段内偏移装载 CS:EIP 寄存器。
中断门描述符,就会发现其中的段内偏移只有 32 位,但是长模式支持 64 位内存寻址,所以要对中断门描述符进行修改和扩展,下面我们就来看看长模式下的中断门描述符的格式,如下图所示。
3.操作系统——CPU的实模式、保护模式和长模式_第10张图片
首先为了支持 64 位寻址中断门描述符在原有基础上增加 8 字节,用于存放目标段偏移的高 32 位值。
其次,目标代码段选择子对应的代码段描述符必须是 64 位的代码段。
长模式也同样在内存中有一个中断门描述符表,只不过表中的条目(如上图所示)是 16 字节大小,最多支持 256 个中断源,对中断的响应和相关权限的检查和保护模式一样,这里不再赘述。

切换到长模式

第一步,准备长模式全局段描述符表。


ex64_GDT:
null_dsc:  dq 0
;第一个段描述符CPU硬件规定必须为0
c64_dsc:dq 0x0020980000000000  ;64位代码段
d64_dsc:dq 0x0000920000000000  ;64位数据段
eGdtLen   equ $ - null_dsc  ;GDT长度
eGdtPtr:dw eGdtLen - 1  ;GDT界限
     dq ex64_GDT

第二步,准备长模式下的 MMU 页表,这个是为了开启分页模式,切换到长模式必须要开启分页,想想看,长模式下已经不对段基址和段长度进行检查了,那么内存地址空间就得不到保护了。


mov eax, cr4
bts eax, 5   ;CR4.PAE = 1
mov cr4, eax ;开启 PAE
mov eax, PAGE_TLB_BADR ;页表物理地址
mov cr3, eax

加载 GDTR 寄存器,使之指向全局段描述表:


lgdt [eGdtPtr]

开启长模式,要同时开启保护模式和分页模式,在实现长模式时定义了 MSR 寄存器,需要用专用的指令 rdmsr、wrmsr 进行读写,IA32_EFER 寄存器的地址为 0xC0000080,它的第 8 位决定了是否开启长模式。


;开启 64位长模式
mov ecx, IA32_EFER
rdmsr
bts eax, 8  ;IA32_EFER.LME =1
wrmsr
;开启 保护模式和分页模式
mov eax, cr0
bts eax, 0    ;CR0.PE =1
bts eax, 31
mov cr0, eax 

进行跳转,加载 CS 段寄存器,刷新其影子寄存器。


jmp 08:entry64 ;entry64为程序标号即64位偏移地址

总结

如果看的云里雾里,了解以下内容即可:

  1. 实模式,是最早期的CPU为了支持单程序运行而实现,仅支持16位地址空间,分段内存模型,对指令不加限制的运行,对内存没有保护隔离作用虽然是16位,但是其借助段寄存器的4位,充分利用了20位的总线,得到了2^20=1M的寻址空间
  2. 保护模式:随着多程序需求的出现,内存需求量不断增加,所以实现了保护模式。包含特权级,对指令及访问的资源做限制,对内存段之间的访问做检查,对中断的响应做检查。扩大到32位内存地址处理32位数据
  3. 长模式:AMD公司在保护模式的基础上把寄存器扩展到64位,使其具有处理64位数据和寻址的能力,弱化了段模式管理,只保留权限检查,忽略段基址和段长度。地址审查交给MMU

你可能感兴趣的:(操作系统,cpu)