操作系统--分页机制的演示

来源:互联网 发布:优惠券淘宝群怎么赚钱 编辑:程序博客网 时间:2024/05/10 05:21

如果还不了解分页机制,请先看这里:分页机制讲解

注:该程序(及相关博文)源于《80X86汇编程序设计  杨季文》

下面给出一个演示如何启用分页管理机制的实例。该实例的逻辑功能是,在屏幕上显示一条表示已启用分页管理机制的提示信息。该实例演示内容包括:初始化页目录表和部分页表;启用分页管理机制;关闭分页管理机制等。该实例假设系统至少有4M字节物理内存。 

1.演示步骤和源程序清单

    为了简单化,实例只有一个任务,并且没有局部描述符表和中断描述符表,不允许中断,也不考虑发生异常,甚至没有使用堆栈。实例执行步骤如下: 
    (1)在实模式下为进入保护模式作初始化;
    (2)切换到保护模式后进入临时代码段,把部分演示代码传送到预定的内存,然后转演示代码段;
    (3)建立页目录表;
    (4)建立页表;
    (5)启用分页管理机制;
    (6)演示在分页管理机制启用后的程序执行和数据存取;
    (7)关闭分页管理机制;
    (8)退出保护模式,结束。
[cpp] view plain copy
  1. .386P  
  2. ;----------------------------------------------------------------------------  
  3. ;打开A20地址线  
  4. ;----------------------------------------------------------------------------  
  5. EnableA20 MACRO  
  6.     push ax  
  7.     in al,92h  
  8.     or al,00000010b  
  9.     out 92h,al  
  10.     pop ax  
  11. ENDM  
  12. ;----------------------------------------------------------------------------  
  13. ;关闭A20地址线  
  14. ;----------------------------------------------------------------------------  
  15. DisableA20 MACRO  
  16.     push ax  
  17.     in al,92h  
  18.     and al,11111101b  
  19.     out 92h,al  
  20.     pop ax  
  21. ENDM  
  22. ;----------------------------------------------------------------------------  
  23. ;16位偏移的段间直接转移指令的宏定义(在16位代码段中使用)  
  24. ;----------------------------------------------------------------------------  
  25. JUMP16 MACRO Selector,Offset  
  26.     DB 0eah ;操作码  
  27.     DW Offset ;16位偏移量  
  28.     DW Selector ;段值或段选择子  
  29. ENDM  
  30. ;----------------------------------------------------------------------------  
  31. ;存储段描述符结构类型定义  
  32. ;----------------------------------------------------------------------------  
  33. Desc STRUC  
  34.     LimitL DW 0 ;段界限(BIT0-15)  
  35.     BaseL DW 0 ;段基地址(BIT0-15)  
  36.     BaseM DB 0 ;段基地址(BIT16-23)  
  37.     Attributes DB 0 ;段属性  
  38.     LimitH DB 0 ;段界限(BIT16-19)(含段属性的高4位)  
  39.     BaseH DB 0 ;段基地址(BIT24-31)  
  40. Desc ENDS  
  41. ;----------------------------------------------------------------------------  
  42. ;伪描述符结构类型定义(用于装入全局或中断描述符表寄存器)  
  43. ;----------------------------------------------------------------------------  
  44. PDesc STRUC  
  45.     Limit DW 0 ;16位界限  
  46.     Base DD 0 ;32位基地址  
  47. PDesc ENDS  
  48. ;----------------------------------------------------------------------------  
  49. ;存储段描述符类型值说明  
  50. ;----------------------------------------------------------------------------  
  51. ATDR EQU 90h ;存在的只读数据段类型值  
  52. ATDW EQU 92h ;存在的可读写数据段属性值  
  53. ATDWA EQU 93h ;存在的已访问可读写数据段类型值  
  54. ATCE EQU 98h ;存在的只执行代码段属性值  
  55. ;----------------------------------------------------------------------------  
  56.   
  57.   
  58.   
  59. ;----------------------------------------------------------------------------  
  60. ;DPL值说明  
  61. ;----------------------------------------------------------------------------  
  62. DPL0 EQU 00h ;DPL=0  
  63. ;----------------------------------------------------------------------------  
  64.   
  65. ;----------------------------------------------------------------------------  
  66.   
  67. ;----------------------------------------------------------------------------  
  68. ;分页机制使用的常量说明  
  69. ;----------------------------------------------------------------------------  
  70. PL EQU 1 ;页存在属性位  
  71. RWR EQU 0 ;R/W属性位值,读/执行  
  72. RWW EQU 2 ;R/W属性位值,读/写/执行  
  73. USS EQU 0 ;U/S属性位值,系统级  
  74. USU EQU 4 ;U/S属性位值,用户级  
  75. ;----------------------------------------------------------------------------  
  76.   
  77.   
  78. ;============================================================================  
  79. PDT_AD          =       200000h ;页目录表所在物理页的地址  
  80. PT0_AD          =       202000h ;页表0所在物理页的地址  
  81. PT1_AD          =       201000h ;页表1所在物理页的地址  
  82. PhVB_AD         =       0b8000h ;物理视频缓冲区地址  
  83. LoVB_AD         =       0f0000h ;程序使用的逻辑视频缓冲区地址  
  84. MPVB_AD         =       301000h ;线性地址0B8000H所映射的物理地址  
  85. PhSC_AD         =       303000h ;部分演示代码所在内存的物理地址  
  86. LoSC_AD         =       402000h ;部分演示代码的逻辑地址  
  87. ;============================================================================  
  88. GDTSeg          SEGMENT PARA USE16                ;全局描述符表数据段(16位)  
  89. ;----------------------------------------------------------------------------  
  90. ;全局描述符表GDT  
  91. GDT             LABEL   BYTE  
  92. ;空描述符  
  93. DUMMY           Desc    <>  
  94. ;规范段描述符及选择子  
  95. Normal          Desc    <0ffffh,,,ATDW,,>  
  96. Normal_Sel      =       Normal-GDT  
  97. ;页目录表所在段描述符(在保护方式下初始化时用)及选择子  
  98. PDT             Desc    <0fffh,PDT_AD AND 0ffffh,PDT_AD SHR 16,ATDW,,>  
  99. PDT_Sel         =       PDT-GDT  
  100. ;页表0所在段描述符(在保护方式下初始化时用)及选择子  
  101. PT0             Desc    <0fffh,PT0_AD AND 0ffffh,PT0_AD SHR 16,ATDW,,>  
  102. PT0_Sel         =       PT0-GDT  
  103. ;页表1所在段描述符(在保护方式下初始化时用)及选择子  
  104. PT1             Desc    <0fffh,PT1_AD AND 0ffffh,PT1_AD SHR 16,ATDW,,>  
  105. PT1_Sel         =       PT1-GDT  
  106. ;逻辑视频缓冲区段描述符及选择子  
  107. LoVideo         Desc    <3999,LoVB_AD AND 0ffffh,LoVB_AD SHR 16,ATDW,,>  
  108. LoVideo_Sel     =       LoVideo-GDT  
  109. ;逻辑上的部分演示代码段的描述符及选择子  
  110. LoCode          Desc    <SCodeLen-1,LoSC_AD AND 0ffffh,LoSC_AD SHR 16,ATCE,,>  
  111. LoCode_Sel      =       LoCode-GDT  
  112. ;预定内存区域(用于部分演示代码)的段描述符及选择子  
  113. TPSCode         Desc    <SCodeLen-1,PhSC_AD AND 0ffffh,PhSC_AD SHR 16,ATDW,,>  
  114. TPSCode_Sel     =       TPSCode-GDT  
  115. ;----------------------------------------------------------------------------  
  116. ;以下是需额外初始化的描述符  
  117. EFFGDT          LABEL   BYTE  
  118. ;临时代码段描述符及选择子  
  119. TempCode        Desc    <0ffffh,TempCodeSeg,,ATCE,,>  
  120. TempCode_Sel    =       TempCode-GDT  
  121. ;演示代码段描述符及选择子  
  122. DemoCode        Desc    <DemoCodeLen-1,DemoCodeSeg,,ATCE,,>  
  123. DemoCode_Sel    =       DemoCode-GDT  
  124. ;演示任务数据段描述符及选择子  
  125. DemoData        Desc    <DemoDataLen-1,DemoDataSeg,,ATDW,,>  
  126. DemoData_Sel    =       DemoData-GDT  
  127. ;初始化时要移动的代码段描述符及选择子(移动时作为数据对待)  
  128. SCode           Desc    <SCodeLen-1,SCodeSeg,,ATDR,,>  
  129. SCode_Sel       =       SCode-GDT  
  130. ;----------------------------------------------------------------------------  
  131. GDTLen          =       $-GDT                     ;全局描述符表长度  
  132. GDNum           =       ($-EFFGDT)/(SIZE Desc)    ;需特殊处理的描述符数  
  133. ;----------------------------------------------------------------------------  
  134. GDTSeg          ENDS                              ;全局描述符表段定义结束  
  135. ;============================================================================  
  136. ;这部分代码在初始化时被复制到预定的内存区域,其功能是在屏幕上显示提示信息  
  137. ;----------------------------------------------------------------------------  
  138. SCodeSeg        SEGMENT PARA USE16  
  139. ASSUME  CS:SCodeSeg,DS:DemoDataSeg  
  140. ;----------------------------------------------------------------------------  
  141. SBegin          PROC    FAR  
  142.     mov     ax,LoVideo_Sel  
  143.     mov     es,ax  
  144.     mov     di,0  
  145.     mov     ah,17h  
  146.     mov     cx,MessLen  
  147. S1:    lodsb  
  148.     stosw  
  149.     loop    S1  
  150.     JUMP16  DemoCode_Sel,Demo3  
  151. SBegin          ENDP  
  152. ;----------------------------------------------------------------------------  
  153. MLen            =       $-SBegin  
  154. SCodeLen        =       $-SCodeSeg  
  155. SCodeSeg        ENDS  
  156. ;============================================================================  
  157. DemoDataSeg     SEGMENT PARA USE16                ;演示任务数据段  
  158. Mess            DB      'Page is OK!'  
  159. MessLen         =       $-Mess  
  160. DemoDataLen     =       $-DemoDataSeg  
  161. DemoDataSeg     ENDS  
  162. ;============================================================================  
  163. DemoCodeSeg     SEGMENT PARA USE16                ;演示任务代码段  
  164. ASSUME  CS:DemoCodeSeg  
  165. ;----------------------------------------------------------------------------  
  166. DemoBegin       PROC    FAR  
  167.     mov     ax,PDT_Sel  
  168.     mov     es,ax  
  169.     xor     di,di  
  170.     mov     cx,1024  
  171.     xor     eax,eax                   ;先把全部表项置成无效  
  172.     rep     stosd                     ;再置表项0和表项1  
  173.     mov     DWORD PTR es:[0],PT0_AD OR (USU+RWW+PL)  
  174.     mov     DWORD PTR es:[4],PT1_AD OR (USU+RWW+PL)  
  175.     mov     ax,PT0_Sel                ;初始化页表0  
  176.     mov     es,ax  
  177.     xor     di,di  
  178.     mov     cx,1024  
  179.     xor     eax,eax  
  180.     or      eax,USU+RWW+PL  
  181. Demo1: stosd  
  182.     add     eax,1000h                 ;先全部置成对应等地址的   
  183.     loop    Demo1                     ;物理页,再特别设置两个表项 1000h=4096  
  184.     mov     di,(PhVB_AD SHR 12)*4  
  185.     mov     DWORD PTR es:[di],MPVB_AD or (USS+RWW+PL)  
  186.     mov     di,(LoVB_AD SHR 12)*4  
  187.     mov     DWORD PTR es:[di],PhVB_AD or (USU+RWR+PL)  
  188.     mov     ax,PT1_Sel                ;初始化页表1  
  189.     mov     es,ax  
  190.     xor     di,di  
  191.     mov     cx,1024  
  192.     mov     eax,400000h  
  193. Demo2: stosd                             ;先把全部表项设置为无效  
  194.     add     eax,1000h  
  195.     loop    Demo2                     ;再特别设置1项  
  196.     mov     di,((LoSC_AD SHR 12)AND 3ffh)*4  
  197.     mov     DWORD PTR es:[di],PhSC_AD or (USU+RWR+PL)  
  198.     mov     eax,PDT_AD  
  199.     mov     cr3,eax  
  200.     mov     eax,cr0  
  201.     or      eax,80000000h  
  202.     mov     cr0,eax  
  203.     jmp     SHORT PageE  
  204. PageE:  mov     ax,DemoData_Sel  
  205.     mov     ds,ax  
  206.     mov     si,OFFSET Mess  
  207.     JUMP16  LoCode_Sel,SBegin  
  208. Demo3:  mov     eax,cr0  
  209.     and     eax,7fffffffh             ;关闭分页机制  
  210.     mov     cr0,eax  
  211.     jmp     SHORT PageD  
  212. PageD:  mov     ax,Normal_Sel  
  213.     JUMP16  TempCode_Sel,ToDOS  
  214. DemoBegin       ENDP  
  215. ;----------------------------------------------------------------------------  
  216. DemoCodeLen     =       $-DemoCodeSeg  
  217. DemoCodeSeg     ENDS  
  218. ;============================================================================  
  219. TempCodeSeg     SEGMENT PARA USE16                ;临时任务的代码段  
  220. ASSUME  CS:TempCodeSeg  
  221. ;----------------------------------------------------------------------------  
  222. Virtual         PROC    FAR  
  223.     cld                               ;为演示在启用分页机制后执  
  224.     mov     ax,SCode_Sel              ;行位于较高线性地址空间中  
  225.     mov     ds,ax                     ;的代码作准备  
  226.     mov     ax,TPSCode_Sel  
  227.     mov     es,ax  
  228.     mov     si,OFFSET SBegin  
  229.     mov     di,si  
  230.     mov     cx,MLen                   ;把分页演示代码复制到预定  
  231.     rep     movsb                     ;内存  
  232.     JUMP16  DemoCode_Sel,DemoBegin  
  233. ToDOS:  mov     ds,ax  
  234.     mov     es,ax  
  235.     mov     eax,cr0                   ;准备返回实模式  
  236.     and     al,11111110b  
  237.     mov     cr0,eax  
  238.     JUMP16  <SEG Real>,<OFFSET Real>  
  239. Virtual         ENDP  
  240. ;----------------------------------------------------------------------------  
  241. TempCodeSeg     ENDS  
  242. ;============================================================================  
  243. RCodeSeg        SEGMENT PARA USE16                ;实方式的初始化代码和数据  
  244. ASSUME  CS:RCodeSeg,DS:RCodeSeg  
  245. ;----------------------------------------------------------------------------  
  246. VGDTR           PDesc   <GDTLen-1,>  
  247. ;----------------------------------------------------------------------------  
  248. Start           PROC  
  249.     push    cs  
  250.     pop     ds  
  251.     cld  
  252.     call    InitGDT                   ;初始化全局描述符表GDT  
  253.     EnableA20  
  254.     lgdt    FWORD PTR VGDTR           ;装载GDTR  
  255.     cli                               ;关中断  
  256.     mov     eax,cr0  
  257.     or      al,1  
  258.     mov     cr0,eax  
  259.     JUMP16  <TempCode_Sel>,<OFFSET Virtual>  
  260. Real:  DisableA20  
  261.     sti  
  262.     mov     ax,4c00h  
  263.     int     21h  
  264. Start           ENDP  
  265. ;----------------------------------------------------------------------------  
  266. InitGDT         PROC  
  267.     push    ds  
  268.     mov     ax,GDTSeg  
  269.     mov     ds,ax  
  270.     mov     cx,GDNum  
  271.     mov     si,OFFSET EFFGDT  
  272. InitG: mov     ax,[si].BaseL  
  273.     movzx   eax,ax  
  274.     shl     eax,4  
  275.     shld    edx,eax,16  
  276.     mov     WORD PTR [si].BaseL,ax  
  277.     mov     BYTE PTR [si].BaseM,dl  
  278.     mov     BYTE PTR [si].BaseH,dh  
  279.     add     si,SIZE Desc  
  280.     loop    InitG  
  281.     pop     ds  
  282.     mov     bx,16  
  283.     mov     ax,GDTSeg  
  284.     mul     bx  
  285.     mov     WORD PTR VGDTR.Base,ax  
  286.     mov     WORD PTR VGDTR.Base+2,dx  
  287.     ret  
  288. InitGDT         ENDP  
  289. ;----------------------------------------------------------------------------  
  290. RCodeSeg        ENDS  
  291. END     Start   

下面仅就演示分页管理机制方面的内容作些说明:

(1)部分演示代码的移动

为了充分说明分页机制所实现的线性地址到物理地址的转换,在初始化时把部分演示代码移动到预定的内存区域。预定的内存区域从 00303000H 开始,即页码为 00303H 的物理页。该部分演示代码的功能是显示指定的字符串。在进入保护模式后做此初始化工作的原因是预定的内存区域在扩展内存中,注意初始化时还没有启用分页机制。

(2)页映射表的初始化

页目录表安排在页码为 00200H 的物理页中,页表 0 安排在页码为 00202H 的物理页中,页表 1 安排在页码为 00201H 的物理页中。演示程序涉及的线性地址空间不超过 007FFFFFH,所以只使用两张页表,为此页目录表中的其它项被置为无效(P=0)。

页表 0 把线性地址空间中的 00000000H—003FFFFFH 映射到物理地址空间中。实例在初始化页表 0 时,使该线性地址空间直接映射到相同地址的物理地址空间,除线性地址空间中页码为 000B8H 和 000F0H 这两页以外。000B8H 页被映射到页码为 00301H 的物理页,而 000F0H 页被映射到页码为 000B8H 的物理页。

页表 1 把线性地址空间中的 00400000H—007FFFFFH 映射到物理地址空间中。实例在初始化页表 1 时,似乎使该线性地址空间直接映射到相同地址的物理地址空间,但是处理对应线性地址空间中 00402H 的表项被另外设置外,其它表项中的 P 位为 0,也即表示对应物理页不存在。初始化后,页表 1 的第 2 项把线性地址空间中的 00402H 页映射到页码为 00303H 的物理页,也就是存放部分演示代码的指定内存区域.下面用一个粗略的示意图展示:



中间为页目录表(其表项为PDT),两边为页表(其表项为PTE),a,b是根据相应的线性地址算出的索引值,其对应索引值里面存了一个地址(为了简便没有设置部分属性(or运算)),指向一个4KB的页(1000h=4096=4KB,所以需要ADD eax ,1000h)。对于图,自己最好把数据填上去,这样更直观,这里为了方便,我没有填上数据。

[cpp] view plain copy
  1. TempCodeSeg     SEGMENT PARA USE16                ;临时任务的代码段  
  2. ASSUME  CS:TempCodeSeg  
  3. ;----------------------------------------------------------------------------  
  4. Virtual         PROC    FAR  
  5.     cld                               ;为演示在启用分页机制后执  
  6.     mov     ax,SCode_Sel              ;行位于较高线性地址空间中  
  7.     mov     ds,ax                     ;的代码作准备  
  8.     mov     ax,TPSCode_Sel  
  9.     mov     es,ax  
  10.     mov     si,OFFSET SBegin  
  11.     mov     di,si  
  12.     mov     cx,MLen                   ;把分页演示代码复制到预定  
  13.     rep     movsb                     ;内存  
  14.     JUMP16  DemoCode_Sel,DemoBegin  

上面这段代码 将演示代码送到TPSCode段选择子所指示的地址(PhSC_AD).

[cpp] view plain copy
  1.     mov     eax,PDT_AD  
  2.     mov     cr3,eax  
  3.     mov     eax,cr0  
  4.     or      eax,80000000h  
  5.     mov     cr0,eax  
  6.     jmp     SHORT PageE  
  7. PageE:  mov     ax,DemoData_Sel  
  8.     mov     ds,ax  
  9.     mov     si,OFFSET Mess  
  10.     JUMP16  LoCode_Sel,SBegin  

根据图片左边分析可得知:线性地址空间中的 00402H 页映射到页码为 00303H 的物理页,所以接下来跳转到线性地址00402的页,这样就映射到了演示代码的页了。

[cpp] view plain copy
  1. mov     di,(PhVB_AD SHR 12)*4  
  2. mov     DWORD PTR es:[di],MPVB_AD or (USS+RWW+PL)  
  3. mov     di,(LoVB_AD SHR 12)*4  
  4. mov     DWORD PTR es:[di],PhVB_AD or (USU+RWR+PL)  

对于这段代码,书上用的不是 or 而是加,这里我觉得or 更正确,这里低位为0,所以加和or 没有区别,但是如果低位不是0这样就不对了。(这是我自己的分析,如果不对还请指教)。

(3)启动分页管理机制
在建立好页映射表后,启用分页机制所要做的操作是简单的,只要把控制寄存器 CR0 中的最高位,也就是 PG 位置 1。具体指令如下:

[cpp] view plain copy
  1. or      eax,80000000h  
  2. mov     cr0,eax  
  3. jmp     SHORT PageE  
  4. ageE:  

在启用分页机制前,线性地址就是物理地址;在启用分页机制后,线性地址要通过分页机制的转换,才成为物理地址。尽管使用一

条转移指令,可清除预取队列,但随后在取指令时使用的线性地址就要经过分页机制转换才成为物理地址。为了顺利过渡,在启用分页机制之后的过渡代码段,仍要维持线性地址等同于物理地址。为了作到这一点,在建立也映射表时,必须使实现过渡的代码所在的线性地址空间页映射到具有相同地址的物理地址空间页。实例中页表 0 就做到了这一点。

(4)关闭分页管理机制
只要把控制寄存器 CR0 中的 PG 位清 0,便关闭了分页机制。在这一过渡阶段,也要保持地址转换前后的一致。

(5)地址转换的演示

在启用分页机制之后,就转移到位于线性地址空间中 00402000H 处开始的代码,该部分代码的功能是显示提示信息"Page is OK!"。实际上这部分代码存放在从物理地址 00303000H 开始的物理内存区域中,是在初始化时被移到此区域的。

在显示提示信息时,要把显示的 ASCII 字符和显示属性填到线性地址空间中 000F0000H 开始的区域中,而不是 000B8000H 开始的区域。从初始化时建立的映射表可见,线性地址空间中的 000F0H 页,被映射到物理地址空间中的 000B8H 页。所以,向线性地址空间中的 000F0H 页写,实际上是向物理地址空间中的 000B8H 页写,也就是真正显示。

(6)页级保护的说明
在进入保护模式之后,特权级一直是 0,所以,无论系统级和用户级页,无论只能读/执行,还是读/执行/写,总是可进行各种形式

的访问。 


程序运行结果展示:



0 0
原创粉丝点击