执行程序的三种模式-创新互联

HelloOS系列之2

企业建站必须是能够以充分展现企业形象为主要目的,是企业文化与产品对外扩展宣传的重要窗口,一个合格的网站不仅仅能为公司带来巨大的互联网上的收集和信息发布平台,创新互联建站面向各种领域:湿喷机成都网站设计成都全网营销推广解决方案、网站设计等建站排名服务。

实模式,保护模式,长模式

我在这里写两种模式,前两种,没写最后一种,原因1是还没看(主要的),另一个原因就是长模式主要用在大型的服务集群,目前我还用不到(优点短视了)。

首先在介绍模式之前,给它们做个排序,由优到差:长模式,保护模式,实模式

再分析模式的 共同的作用,就是通过数据来运行程序,程序也就是进程了。那么程序和数据的存入和取出就是关键了。也是这几个模式主要干的事。

这三个模式是个逐渐优化的过程。

  1. 实模式

实模式,就是打开计算机开机键,什么也不做,直接进入的模式,也就是我现在正在用到的模式。话说他应该是很优秀的啊,这个我也不知道,感觉这篇浅尝辄止了。。。

  1. 过去的实模式,以及过去的计算机:

过去计算机很大很大,计算机主要是为方便计算高位计算的,什么是高位计算呢?就是123456789 * 123456789 = ???我管这个叫做高位计算,只计算一次,这要这个结果,看下面的程序:

int main()

{

int* addr = (int*)0;

cli(); //关中断

while(1)

{

*addr = 0;

addr++;

}

return 0;

}

这个程序做了两件事情,第一关CPU中断,第二是在这个无限循环中每次都清空了内存,我管这个叫大半是人工,计算机充当一个力量巨大的蛮力工人。

其实这也反应了实模式的弊端:一个方面是运行真实的指令,对指令的动作不作区分,直接执行指令的真实功能,另一方面是发往内存的地址是真实的,对任何地址不加限制地发往内存。(这就相当于直接往物理层中写)。

实模式下面的寄存器:

  1. 首先寄存器和内存是互相存取的(寄存器 ->内存, 寄存器<- 内存),因为计算机如果直接从内存中存取数据,太慢,所以寄存器就诞生了,他把计算机中准备用到的数据先拿到,等待被存取,性能会变快。
  2. 实模式是分段模型,何为分段模型,它存在的意义?

分段模型是分代码段和数据段的,代码段也就是执行的指令,而数据段是存放的数据。代码段使用的是CS和IP寄存器,数据段使用的是ss和sp寄存器,如果这两中数据都写到一块内存,一个寄存器去存取,会乱掉。。。

下图中每种寄存器都是16bit.

何为段寄存器?段:顾名思义,肯定有段基址,和段长,再内存中这段A表示代码,那段B表示数据。

下面是一段helloworld汇编代码:

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(20220710:21h是软中断号,这条命令应该是开启中断)

MOV AX,4C00h ;给AX设置参数4C00h(20220710补充4C00h是什么意思)

INT 21h      ;调用4C00h号功能,结束程序(20220710:这条应该是关闭中断)

code ENDS

END start

这段代码逻辑很清晰了:

我在解读一下:

  1. 首先定义一个数据段放”helloworld”
  2. 然后定义一个代码段放指令
  3. 让DS通用寄存器把“helloworld“存进去
  4. 然后执行终端, DS中的指针指向hello的首地址,然后移动指针
  5. 中断关闭,结束程序。

那么问题来了,把数据存放进寄存中,之后为何中断?实模式下中断是个什么机制?

  1. 中断的目的是,中断当前的进程,CPU执行现在接受到的信号,需要CPU跳转到特定的地址上,执行特定的代码。
  2. 实模式下CPU中断先保存代码段也就是CS和IP指针寄存器(这个代码段是当前执行的进程,没开启中断之前的),然后再去装载新的CS和IP寄存器中的代码段,执行特定的程序。
  3. 分为硬件中断和软中断,

第一种情况是,中断控制器给 CPU 发送了一个电子信号,CPU 会对这个信号作出应答。随后中断控制器会将中断号发送给 CPU,这是硬件中断。

第二种情况就是 CPU 执行了 INT 指令,这个指令后面会跟随一个常数,这个常数即是软中断号。这种情况是软件中断。

其实很简单,中断表是操作系统存在内存的,可以理解为一堆switch,INT/中断就是命中一个switch,然后会执行特定的执行体,是不是和软件很像,事实本质没区别。

  1. 如何进行中断,就是如何去找将要执行的代码段呢?

实模式下,有个中断向量表,这个表的地址和长度由 CPU 的特定寄存器 IDTR 指向。实模式下,表中的一个条目由代码段地址和段内偏移组成,这个中断是个啥?自己写的呗,写啥是啥?中断描述符表寄存器(Interruptions Descriptor Table Register)IDTR,它是个专门存放中断描述符的,中断描述符是个标志性的符号(20220710:中断号应该算是其中一种吧),看见了,就中断了,表意味着,有中断表长度和地址(20220710长度如何计算,通过段内偏移;而地址应该是段基地址,通过这两个计算指定命令或是数据占用内存多少字节)。有了中断号以后,CPU 就能根据 IDTR 寄存器中的信息(20220710:中断号是标志,中断号从何而来,是通过计算机中的服务等吧,通过中断号去执行这个中断代表的含义,是软件部分的代码执行,中断是硬件和软件的交流方式,也是软件中进程与进程的交流方式,详见文末符表,列出几个中断号代表的含义),计算出中断向量中的条目,进而装载 CS(装入代码段基地址)、IP(装入代码段内偏移)寄存器,最终响应中断。

总结:上述是个完整的存取数据指令,相应中断的过程。首先实模式是段模式,意味着包含段基地址和段内偏移,然后实模式将代码和数据存放到不同的寄存器中进行存取,接着数据存进寄存器之后,CPU如何相应并且执行,?答曰:通过中断,而中断内部如何获取到代码段的地址呢?答曰通过中断表。实模式的弊端是,实,真实的地址(物理地址),真实的指令,有指令就执行,不加控制。

  1. 保护模式

针对实模式的弊端,保护模式来了。。。

保护模式加了一些控制寄存器和段寄存器,特权模式,意味着不是什么指令都可以将计算数据的。

下图中的可以看到最后一行,

保护模式下的四种特权级。

特权级分为 4 级,R0~R3,每个特权级执行指令的数量不同,R0 可以执行所有指令,R1、R2、R3 依次递减,它们只能执行上一级指令数量的子集。而内存的访问则是靠后面所说的段描述符和特权级相互配合去实现的。

下面的圆环图,从外到内,既能体现权力的大小,又能体现各特权级对资源控制访问的多少,还能体现各特权级之间的包含关系。R0 拥有大权力,可以访问低特权级的资源,反之则不行。

目前为止,内存还是分段模型,要对内存进行保护,就可以转换成对段的保护。由于 CPU 的扩展导致了 32 位的段基地址和段内偏移,还有一些其它信息,所以 16 位的段寄存器肯定放不下。放不下就要找内存借空间,然后把描述一个段的信息封装成特定格式的段描述符,放在内存中,其格式如下。

问题来了,为什么放不下?因为加了控制寄存器和它的段寄存器。

(20220710:内存中的段基址15:0,加上)

多个段描述符在内存中形成全局段描述符表,该表的基地址和长度由 CPU 和 GDTR 寄存器指示。段寄存器中不再存放段基地址,而是具体段描述符的索引,访问一个内存地址时,段寄存器中的索引首先会结合 GDTR 寄存器找到内存中的段描述符,再根据其中的段信息判断能不能访问成功。(GDTR:Globle Descriptor Table Registers)

1. 从GDTR中找到GDT的地址 2. 从段寄存器中找到要访问的段描述符索引 3. 用该描述符索引去GDT中找到具体的段描述符 4. 根据描述符中的段信息判断能否访问成功。

首次找段描述符的作用,是想找段信息,那为什么不直接就找短信息,而还要通过段描述符呢?答曰:段描述符相当于一个bool值,当指针访问这个段寄存器中的地址(也就是段描述符)时,访问成功了,说明这条指令有权限,否则,无。

其实现在还是有个问题,由于 CPU 的扩展导致了 32 位的段基地址和段内偏移,还有一些其它信息,所以 16 位的段寄存器肯定放不下,那就借了内存的空间,而内存存取数据性能较低,速度慢,怎么办?是真的把控制寄存器和它的段地址寄存器放到内存了,答曰:不是滴。影子寄存器——高速缓存,

看下原文:影子寄存器是靠硬件来操作的,对系统程序员不可见,是硬件为了减少性能损耗而设计的一个段描述符的高速缓存,不然每次内存访问都要去内存中查表,那性能损失是巨大的,影子寄存器也正好是 64 位,里面存放了 8 字节段描述符数据。

低三位之所以能放 TI 和 RPL,是因为段描述符 8 字节对齐,每个索引低 3 位都为 0,我们不用关注 LDT,只需要使用 GDT 全局描述符表,所以 TI 永远设为 0。

通常情况下,CS 和 SS 中 RPL 就组成了 CPL(当前权限级别),所以常常是 RPL=CPL,进而 CPL 就表示发起访问者要以什么权限去访问目标段,当 CPL 大于目标段 DPL 时,则 CPU 禁止访问,只有 CPL 小于等于目标段 DPL 时才能访问。

这段话的意思是,特权就4级(0-3)超了就不行。。。

上述是数据指令存取,下述是中断:

实模式下 CPU 不需要做权限检查,所以它可以直接通过中断向量表中的值装载 CS:IP 寄存器就好了。

而保护模式下的中断要权限检查,还有特权级的切换,所以就需要扩展中断向量表的信息,即每个中断用一个中断门描述符来表示,也可以简称为中断门,中断门描述符依然有自己的格式。

同样的,保护模式要实现中断,也必须在内存中有一个中断向量表,同样是由 IDTR 寄存器指向,只不过中断向量表中的条目变成了中断门描述符。

我的理解:保护模式多了权限检查,因此再保护模式下,中断向量表就不会放代码基地址和偏移啥的了,而是放中断描述符(就是那个所谓的bool值)。

接着,检查中断门描述符中的段选择子指向的段描述符。

最后做权限检查,如果 CPL 小于等于中断门的 DPL,并且 CPL 大于等于中断门中的段选择子所指向的段描述符的 DPL,就指向段描述符的 DPL。

进一步的,CPL 等于中断门中的段选择子指向段描述符的 DPL,则为同级权限不进行栈切换,否则进行栈切换。

如果进行栈切换,还需要从 TSS 中加载具体权限的 SS、ESP,当然也要对 SS 中段选择子指向的段描述符进行检查。

做完这一系列检查之后,CPU 才会加载中断门描述符中目标代码段选择子到 CS 寄存器中,把目标代码段偏移加载到 EIP 寄存器中。

这段说的就是特权就4级,别超了。。。

最后就是实模式切换到保护模式:(我还没做实验,不知道真假)

Step1.    x86 CPU 在第一次加电和每次 reset 后,都会自动进入实模式,要想进入保护模式,就需要程序员写代码实现从实模式切换到保护模式。切换到保护模式的步骤如下。第一步,准备全局段描述符表,代码如下。

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

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

lgdt [GDT_PTR]

Step3.     设置 CR0 寄存器,开启保护模式。

;开启 PE

mov eax, cr0

bts eax, 0                      ; CR0.PE =1

mov cr0, eax        

Step4.     进行长跳转,加载 CS 段寄存器,即段选择子。

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

你也许会有疑问,为什么要进行长跳转,这是因为我们无法直接或间接 mov 一个数据到 CS 寄存器中,因为刚刚开启保护模式时,CS 的影子寄存器还是实模式下的值,所以需要告诉 CPU 加载新的段信息。接下来,CPU 发现了 CRO 寄存器第 0 位的值是 1,就会按 GDTR 的指示找到全局描述符表,然后根据索引值 8,把新的段描述符信息加载到 CS 影子寄存器,当然这里的前提是进行一系列合法的检查。

加餐,我不太懂:

保护模式平坦模型:

分段模型有很多缺陷,这在后面课程讲内存管理时有详细介绍,其实现代操作系统都会使用分页模型(这点在后面讲 MMU 那节课再探讨)。但是 x86 CPU 并不能直接使用分页模型,而是要在分段模型的前提下,根据需要决定是否要开启分页。因为这是硬件的规定,程序员是无法改变的。但是我们可以简化设计,来使分段成为一种“虚设”,这就是保护模式的平坦模型。根据前面的描述,我们发现 CPU32 位的寄存器最多只能产生 4GB 大小的地址,而一个段长度也只能是 4GB,所以我们把所有段的基地址设为 0,段的长度设为 0xFFFFF,段长度的粒度设为 4KB,这样所有的段都指向同一个((段的长度 +1)* 粒度 - 1)字节大小的地址空间。下面我们还是看一看前面 Hello OS 中段描述符表,如下所示。

GDT_START:

knull_dsc: dq 0

;第一个段描述符CPU硬件规定必须为0

kcode_dsc: dq 0x00cf9e000000ffff

;段基地址=0,段长度=0xfffff

;G=1,D/B=1,L=0,AVL=0

;P=1,DPL=0,S=1

;T=1,C=1,R=1,A=0

kdata_dsc: dq 0x00cf92000000ffff

;段基地址=0,段长度=0xfffff

;G=1,D/B=1,L=0,AVL=0

;P=1,DPL=0,S=1

;T=0,C=0,R=1,A=0

GDT_END:

GDT_PTR:

GDTLEN  dw GDT_END-GDT_START-1

GDTBASE  dd GDT_START

上面代码中注释已经很明白了,段长度需要和 G 位配合,若 G 位为 1 则段长度等于 0xfffff 个 4KB。上面段描述符的 DPL=0,这说明需要最高权限即 CPL=0 才能访问

附表1

表1 常见的PC中断

———————————

中断(hex)          描述

5                 屏幕打印服务

10                视频显示服务(MDA,CGA,EGA,VGA)

11                获得设备清单

12                获得内存大小

13                磁盘服务

14                串行口服务

15                杂项功能服务

16                键盘服务

17                打印机服务

1A                时钟服务

21                DOS函数

2F                DOS多路共享服务

33                鼠标器服务

67                EMS服务

篇尾语:感谢彭东大神,如果有小伙伴转载请联系我

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


文章名称:执行程序的三种模式-创新互联
浏览地址:http://hbruida.cn/article/ejced.html