操作系统实践(4)——从保护模式返回实模式

来源:互联网 发布:怎么注册淘宝客账号 编辑:程序博客网 时间:2024/06/11 02:57

好吧,一开始我觉得从保护模式应该就是设置一下cr0然后jmp一下就回到实模式了,这个没必要去实践。不过看到书中里面有些代码觉得有些冗余,然后里面有段话也是没看明白,动手实践才恍然大悟。


于渊在书中写这个例子的思路大概是这样的:
1. 从实模式中跳转进入保护模式。(参考前面几篇)
2. 在保护模式下,访问一个地址偏大点的(实模式寻址范围之外)内存块进行读写,验证一下保护模式寻址范围是不是比实模式寻址大。
3. 返回实模式,返回实模式是这段代码:

; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式[SECTION .s16code]ALIGN   32[BITS   16]LABEL_SEG_CODE16:    ; 跳回实模式:    mov ax, SelectorNormal    mov ds, ax    mov es, ax    mov fs, ax    mov gs, ax    mov ss, ax    mov eax, cr0    and al, 11111110b    mov cr0, eaxLABEL_GO_BACK_TO_REAL:    jmp 0:LABEL_REAL_ENTRY  ; 段地址会在程序开始处被设置成正确的值Code16Len   equ $ - LABEL_SEG_CODE16; END of [SECTION .s16code]

而跳回实模式之后执行的代码如下:

LABEL_REAL_ENTRY:       ; 从保护模式跳回到实模式就到了这里    mov ax, cs    mov ds, ax    mov es, ax    mov ss, ax    mov sp, [SPValueInRealMode]    in  al, 92h         ;    and al, 11111101b   ;  | 关闭 A20 地址线    out 92h, al         ;     sti         ; 开中断    mov ax, 4c00h   ;     int 21h         ;   回到 DOS; END of [SECTION .s16]

问题

  1. 跳回实模式前,设置ds、es、fs、gs、ss有什么作用,跳回实模式之后不是又设置了ds、es等寄存器了么?
    原文描述:

    “在准备结束保护模式回到实模式前,需要加载一个合适的描述符选择子到有关的段寄存器,以使对应段描述符高速缓冲寄存器中案有合适的段界限和属性。”

    80286开始,保护模式下,为避免每次形成线性地址的时时,都要使用选择子相关的信息而去访问内存,cpu配备了相应的段描述符高速缓冲寄存器。
    所以重新加载一下ds、es、ss相当于清缓存!

  2. 为什么这里跳回实模式的代码段必须是在16位的代码段中?
    原文如下:

    “不能从32位代码段返回实模式,只能从16位代码段中返回。这是因为无法实现从32为位代码段返回时CS高速缓冲寄存器中的属性符合实模式的要求(实模式不能改变段属性)。”

    我的理解,原因还是跟问题1一样,如果在32位的代码段中要跳回16位的实模式,那么设置完cr0后,准备用jmp跳回实模式的地址去,但是此时的cs相关的段描述符高速缓冲里面的属性还是32位保护模式的段属性,必须跟上个问题一样,清一下缓存,但是jmp跳过去之后是16位的实模式了,没法清缓存了!
    后来看了篇文章,与我的理解一致:关于从保护模式切换到实模式的相关说明 。

  3. 从32位的代码段跳回实模式不行吗?
    从问题2我们知道,保护模式跳回实模式关键的一点,就是要保证跳回实模式后,相关的段的段属性是正确的。之前有个想法就是,从32位的代码段跳回实模式,然后使跳回实模式后代码段属性符合16位实模式下不就行了么?因为跳回去实模式后已经无法修改相关的段属性,所以必须在跳回实模式之前设置好,那么只能在段描述符中设置好,但这有个问题,如果这个描述符标志的是32位的段,从这个32位的段跳回后就无法修改了,所以这个描述符就只能是标志为16位的段。
  4. jmp 0:LABEL_REAL_ENTRY ; 段地址会在程序开始处被设置成正确的值这怎么实现的?
    好吧,这是个小技巧。思路是这样,当这块代码读入内存后,直接修改内存中这个指令。(指令与数据,只是人为的区分,在内存中,它们就都只是二进制数据)
0 0
原创粉丝点击