24、存储器的保护


上一节:23、指令的格式及其操作尺寸
下一节:25、保护模式程序的动态加载和执行

01、MOV DS, AX和MOV DS, EAX

具体代码参考c11_mbr.asm。视频里的程序和配套资料里的代码还不一样,但是基本功能类似。
计算GDT逻辑段地址中使用除法div r/m32
在这里插入图片描述
其中指令mov ds, ax在16位操作尺寸下机器码是8ED8,在32位操作尺寸下机器码是668ED8,但是Intel的官方文档中对这种指令做了优化:
在这里插入图片描述
大致意思就是在16位和32位操作尺寸下,这条指令的机器码都是8ED8。若加上前缀66那么执行时需要多花一个执行周期。

所以Intel官方手册建议使用mov ds, eax来防止出现指令前缀66,但是使用的NASM编译器,不管操作尺寸是16位还是32位,编译之后都不会出现指令前缀66

编译之后查看lst列表文件:
使用16位操作尺寸时:
在这里插入图片描述
使用32位操作尺寸时:
在这里插入图片描述
可以看出NASM编译器确实将这种指令编译成了8ED8

此节程序就是设置栈、计算GDT所在逻辑段地址、安装描述符、初始化GDTR、打开A20、禁止中断、设置CR0进入保护模式、跳转执行保护模式程序。

02、修改段寄存器时的保护

在上一节最后进入到保护模式之后,执行了远转移指令jmp,此时给出的段选择子的T位都是0,即要选择的描述符在全局描述符表GDT中。同时也会做一些检查以确保选择描述符这一系列的过程正确。
在这里插入图片描述
在这里插入图片描述
1、检查第一步,如上图,处理器会检查访问的描述符的边界,若超过了边界条件,就会产生一个异常中断13,同时段寄存器保持原来的值不变。
2、确定描述符的类型,比如描述符S=1、X=1,表示此描述符代表一个代码段,只能加载到段寄存器CS。首先描述符类型(TYPE)字段必须是有效的,000就是无效的;接着检查描述符的类型是否与段寄存器匹配,参照下表进行检查:
在这里插入图片描述
3、检查描述符中的P位是否为1,若为0表示虽然描述符已经定义,但是对应的段并不在物理内存中,此时处理器终止处理并引发11号中断。一般来说需要定义一个中断处理程序接管11号中断,将对应的描述符所指示的段调入内存,然后将P位置1。这种类型的中断返回时并不是返回到原先的下一条指令,而是返回原先的指令,这样就能重新加载描述符。
P位是1,处理器将描述符加载到段描述符对应的描述符高速缓存器中,同时将A位置1。
4、一旦上述验证通过,处理器就将段选择子加载到段寄存器段选择器中,

其中还有一些注意事项:
在这里插入图片描述

03、代码段执行时的保护

进入保护模式之后远转移指令jmp 0x0010:flush会修改段寄存器CS,会进行一系列检查工作:

  • 首先选择子0x0010指定的描述符不能超过描述符表的边界;
  • 其次指定的描述符必须是代码段描述符,相关的信息必须是完整且合法的;
  • 接着还要检查偏移量flush是否超出了当前段的界限。因为是刚进入保护模式,CS描述符高速缓存器的内容还是之前的值,段界限是0xFFFF

一旦通过了检查,就将选择子0x0010带入CS段选择器,并用描述符填充描述符高速缓存器,接着用偏移量flush修改指令指针寄存器EIP,处理器立即转入目标位置处执行。

因为这一条指令刷新了段寄存器CS,导致处理器到一个全新的代码段执行。

这个描述符指定了D位是1,因为这个描述符所指定的段在执行jmp指令之前是在16位操作尺寸下运行的,之后是在32位操作尺寸下运行的。
在这里插入图片描述
在保护模式下一旦相应的描述符被加载到CS描述符高速缓存器,则处理器取指令和执行指令时就不再访问描述符表,而是直接使用CS描述符高速缓存器,从中取得线性基地址同指令指针寄存器EIP的值相加形成32位线性基地址,到内存中取得下一条指令。

不过在执行下一条指令执行之前,处理器必须检查这条指令的地址是否有效,以防止执行处理器不允许的指令。
在这里插入图片描述

04、用向上扩展的段做为栈段

选择01号段:
在这里插入图片描述
01号段描述符的具体状态如下:
在这里插入图片描述

  • 基地址:为0x00000000
  • S位为1
  • X位为0,表示数据段
  • E位为0,表示向上扩展
  • 段界限为0xFFFFF
  • G位为1,表示段的粒度为4K字节,段界限是以4K字节为单位
    在这里插入图片描述
    这与之前的段重叠了,我们知道代码段是不允许写入的,指的是不允许通过代码段的描述符来写入代码段,但是可以通过这个重叠的4G字节的段来写入代码段。

接下来设置栈段:
在这里插入图片描述
选择的是11号描述符:
在这里插入图片描述

  • X位为0,表示数据段
  • B位由于这是数据段):为1,使用ESP,(为0使用SP
  • E位为0,表示向上扩展的
  • 基地址:为0x00006C00
  • 段界限:为0x007FF
  • G位为0,表示段界限按照字节划分
    在这里插入图片描述
    这个栈段是数据段,共2K字节。

栈的扩展方向推进方向不同,段扩展方向不是数据的读写方向,而是用来定义偏移量的范围、处理器的界限检查等,如下:
在这里插入图片描述
因为这是数据段、所以是B位,且B位的值为1,表示使用ESP当作栈的指针,所以要设置ESP的初始值为段地大小(段界限0x7FF + 1 = 0x800)。

05、向上扩展的段作为栈段时的保护

上一节中使用的栈段为2K字节:
在这里插入图片描述
且使用ESP当做栈指针寄存器来进行隐式的栈操作指令,如:
在这里插入图片描述
这些指令操作时,会对栈进行压栈、出栈。

使用下列两条代码演示栈的检查:
在这里插入图片描述
在压入时选哟对偏移量进行检查:
在这里插入图片描述
第一条指令压栈后的状态,和检查过程:
在这里插入图片描述
在这里插入图片描述
若将ESP初始值设置为0x802,再进行压栈指令时就会将数据压入到栈的边界之外,就会导致处理器产生中断。

06、访问普通数据段时的保护

将上一节压入的数据弹出:
在这里插入图片描述
当前栈的布局:
在这里插入图片描述
首先需要检查出栈的数据是否超出栈的边界之外:
在这里插入图片描述
检查通过之后:
1、使用段SS的基地址0x00006C00 加上 当前ESP中的偏移值0x07F8 得到有效地址0x000073F8,从这个地址处取出4个字节数据。
2、之后使用默认的数据段DS段地址0x00000000加上指令中给出的偏移量0x0B800得到有效地址为0x000B8000,将栈中弹出的数据写入此处。
在这里插入图片描述
在写入之前,处理器要检查是否会超出段界限之外:
在这里插入图片描述
写入之后数据段DS的状态:
在这里插入图片描述
3、出栈之后,处理器将ESP的值加4变为0x07FC
在这里插入图片描述

07、内存线性地址的回绕特性

段的逻辑地址为0x0000000,加上右边的偏移量,得到逻辑地址。
在这里插入图片描述
在这里插入图片描述
还可以使用下列方式表示偏移量:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

08、用向下扩展的段做为栈段

之前使用向上扩展的段作为栈段,大小为2K字节。
在这里插入图片描述
描述符:
在这里插入图片描述

  • S位:为1,表示为寄存器的段描述符
  • X位:为0表示数据段描述符,为1表示代码段描述符
  • E位:为0表示向上扩展,为1表示向下扩展
    向下扩展的数据段实际允许使用的偏移量范围 = 实际允许使用的段界限 + 1开始的,段内偏移量的最大值为0xFFFF0xFFFFFFFF
    在这里插入图片描述
  • D/B位:对于代码段是D位、数据段是B位,B位为0表示段内偏移量的最大值是0xFFFFB位为1表示段内偏移量的最大值是0xFFFFFFFF
    还有B位为0使用SP为1使用ESP
    在这里插入图片描述
    对于向下扩展的段:
    在这里插入图片描述
    程序中使用第二个栈段:
    在这里插入图片描述
    程序中使用第二个栈段描述符:
    在这里插入图片描述
    在这里插入图片描述
  • S位为1表示寄存器的段描述符
  • X位为0表示数据段
  • 基地址:为0x00007C00
  • E位为1表示向下扩展
  • W位为1表示可读可写
  • 段界限:为0xFFFFE
  • G位为1表示粒度为4K字节,即段界限以4K字节为单位
  • B位为1表示偏移量理论最大值是0xFFFFFFFF,为1也表示使用ESP

这个新的段,状态如下:
在这里插入图片描述
ESP的值要设置为站的最大界线值 + 1:0xFFFFFFFF + 1 = 0x00000000

09、向下扩展的段作为栈段时的保护

上一节中设置的栈:
在这里插入图片描述
使用下列指令执行压栈操作:
在这里插入图片描述
在执行压栈指令时,需要的压入的数据进行检查,以确定不会超出端的界限:
在这里插入图片描述
具体为处理器先将ESP4,即0 - 4 = 0xFFFFFFC,则0xFFFFFFC > 实际使用的段界限0xFFFFEFFF,符合上述条件执行压栈操作。

接下来处理器使用段的基地址0x00007C00加上操作数的有效地址0xFFFFFFFC = 0x00007BFC,从这里写入数据。
在这里插入图片描述
压栈之后执行出栈操作;
在这里插入图片描述
具体操作过程参考第05节即可。

10、通过别名来实现段的共用和共享

在保护模式下不能使用CS来修改内存,但是可以设置一个代码段的别名描述符,将其X位设置为0表示数据段,这样就可通过ES来访问字符串所在内存位置,以达到修改内存的操作。
在这里插入图片描述

11、冒泡排序的基本原理

排序5、9、3、7:比较次数为长度4减1为3次
第一遍比较:选出最大数字9
5、9、3、7
5、3、9、7
5、3、7、9
第二遍比较:选出最大数字7
3、5、7
3、5、7
第三遍比较:选出最大数字5
3、5
最后排序完成为:3、5、7、9
在这里插入图片描述

12、32位操作尺寸下的LOOP指令

具体代码为c12_mbr.asm

CS高速缓存寄存器中的D位为0表示使用16位操作尺寸,那么loop指令使用cx计数;D位为1表示使用32位操作尺寸,那么loop指令使用ecx计数,

冒泡排序外循环:@@1
在这里插入图片描述
冒泡排序内循环:@@2
在这里插入图片描述
其中外循环比较次数:可以看出内循环比较次数和外循环ECX相同。
在这里插入图片描述
冒泡内循环比较过程:
在这里插入图片描述

13、数据交换指令XCHG

具体代码为c12_mbr.asm
XCHG指令:操作数不能同时为内存地址
在这里插入图片描述
上一节:23、指令的格式及其操作尺寸
下一节:25、保护模式程序的动态加载和执行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

单单宁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值