利用存储器的保护功能,可实现有些有价值的功能,比如虚拟内存管理。当处理器访问一个实际上不存在的段时,会引发异常中断。 *** 作系统可利用这一点,通过接管异常处理过程,并用硬盘来进行段的换入和换出。实现较小内存空间运行尽可能大,尽可能多的程序。本章目标:
1.通过实例认识处理器如何进行存储器保护
2.了解别名段意义和作用
3.以字符串排序过程为例子,演示保护模式下内存数据访问,体验它们与在实模式下访问数据段不同。
12.1.代码清单12-1 12.2.进入32位保护模式 12.2.1.话说mov ds, ax和mov ds, eax我们知道,段寄存器【选择器】的值只能用内存单元或通用寄存器来传送,一般指令格式为:
mov sreg, r/m16
这里有一个常见例子:
mov ds, ax
在16位模式和32位模式下,一些老式的编译器会生成不同的机器代码。
[bits 16]
mov ds, ax ; 8E D8
[bits 32]
mov ds, ax ; 66 8E D8
由于在16位模式下,默认的 *** 作数大小是字【2字节】,故生成8E D8。在32位模式下,默认的 *** 作数大小是双字【4字节】。由于指令中的源 *** 作数是16位的AX,故编译后的机器码前面应添加前缀0x66以反转默认的 *** 作数大小,即66 8E D8。
它们在16位模式和32位模式下的机器指令被设计为相同,即都是8E D8,不需要指令前缀。
很多编译器认为,32位模式下,源 *** 作数是16位的寄存器AX时,应添加指令前缀。
mov ds, eax
对NASM编译器,不管处理器模式如何,不管指令形式如何,以下代码编译后的结果都一样。
[bits 16]
mov ds, ax ; 8E D8
mov ds, eax; 8E D8
[bits 32]
mov ds, ax ; 8E D8
mov ds, eax; 8E D8
12.2.2.创建GDT并安装段描述符
在32位处理器上,即使在实模式下,也可用32位寄存器。
32位处理器可执行以下除法 *** 作:
div r/m32
其中,64位的被除数在EDX:EAX中,32位被除数可以在32位通用寄存器中,也可在32位内存单元中。指令执行后,EAX中的商是段地址,仅低16位有效;EDX中的余数是段内偏移地址,仅低16位有效。
对段的粒度为4KB的段,描述符中的界限值加1,就是该段有多少个4KB。因此,实际使用的段界限为:
(描述符中的段界限值+1) * 0x1000 - 1
对向上扩展的段,段界限数值上等于段的长度减去1。
32位代码,16位代码。
如果需访问代码段内的数据,只能重新为该段安装一个新的描述符,并将其定义为可读可写的数据段。需写时,通过新的描述符来进行。
当两个以上的描述符都描述和指向同一个段时,把另外的描述符称为别名。如果两个程序想共享同一个内存区域,可分别为每个程序都创建一个描述符,且它们都指向同一个内存段,这也是别名应用的例子。
GDT界限值为总尺寸-1。
需要对代码段做修改时。只能重新针对此段定义一个新的有写权限描述符,通过此描述符来写。别名技术除了用于读写代码段。如果两个程序共享一个内存区域,可分别为每个程序都创建一个描述符,且都指向同一个内存段,这也是别名应用例子。
12.3.修改段寄存器时的保护进行段描述符设置时,处理器会进行范围合法性检查,类别检查【代码段不可加载到除CS外的其他段寄存器中】。
描述符的类别是否和段寄存器的用途匹配。
还要检查描述符的P位。如P=0,表面虽然描述符已被定义,但该段实际上并不存在物理内存中。此时,处理器中止处理,引发异常中断11。一般,应定义一个中断处理程序,把该描述符所对应的段从硬盘等外部存储器调入内存,然后置P位。中断返回时,再次尝试刚才 *** 作。
如P=1,则处理器将描述符加载到段寄存器的描述符高速缓存器,同时置A位【仅限当前讨论的存储器的段描述符】。
一旦 上述规则验证通过,处理器就将选择子加载到段寄存器的选择器。只有可写入的数据段才能加载到SS,CS只允许加载代码段描述符。对DS,ES,FS和GS,可向其加载数值为0的选择子。
12.4.地址变换时的保护 12.4.1.代码段执行时的保护0 <= (EIP+指令长度-1) <= 实际使用的段界限
实际使用段界限计算:
描述符的G=0,实际使用的段界限就是描述符中记载的段界限
描述符的G=1,实际使用的段界限=描述符中的段界限值*0x1000+0xFFF
代码段不可写入。只有在代码段可读下,才能由指令读取其内容。
12.4.2.栈 *** 作时的保护现在只讨论向下扩展的段。后续会遇到向上扩展的段。
实际使用段界限计算:
描述符的G=0,实际使用的段界限就是描述符中记载的段界限
描述符的G=1,实际使用的段界限=描述符中的段界限值*0x1000+0xFFF
实际使用的段界限就是段内不允许访问的最低端偏移地址。最高端地址,没有限制。最大可以是0xFFFFFFFF。
12.4.3.数据访问时的保护实际使用的段界限+1 <= (ESP的内容 - 操作数的长度) <= 0xFFFFFFFF
这里的数据段,特指向上扩展的数据段,有别于栈和向下扩展的数据段。
12.5.使用别名访问代码段对字符排序0 <= (EA+操作数大小-1) <= 实际使用的段界限。EA是内存单元有效地址,可以通过立即数,寄存器,或符合方式给出。
32位模式下,如指令的 *** 作数是16位的,要加前缀0x66。相似地,在32位模式下,如要在指令中使用16位的有效地址,需为该指令添加前缀0x67。因此,当指令:
mov eax, [bx]
用bits 32编译后,有指令前缀0x67
xchg是交换指令,用于交换两个 *** 作数的内容,源 *** 作数和目的 *** 作数都可是8/16/32位的寄存器,或指向8/16/32位实际 *** 作数的内存单元地址,但不允许两者同时为内存地址。格式为:
xchg r/m8, r8
xchg r/m16, r16
xchg r/m32,r32
xchg r8, m8
xchg r16, m16
xchg r32, m32
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)