x86   保护模式  十  分页管理机制

8.386开始支持分页管理机制

段机制实现虚拟地址到线性地址的转换,分页机制实现线性地址到物理地址的转换。如果不启用分页,那么线性就是物理地址

一  分页管理简介

  cr0中的pg位=1   分页生效,将线性地址转换为物理地址。

  只有在保证pe位为1的情况下  才能使pg位为1  否则将引起通用保护故障。

  分页机制将线性地址空间和物理地址空间分别划分为大小相同的块。这样的块称为页。线性的页和物理的页之间建立映射,完成线性到物理地址的转换。线性地址空间的页与物理地址空间之间的映射可根据需要而确定,可根据需要而改变。线性地址空间的任何一页,可以映射为物理地址空间中的任何一页。

  采用分页机制的主要目的是便于实现虚拟存储器,不像段的大小可变,页的大小是固定的大小相等的,根据程序的逻辑划分段,根据实现虚拟存储器方便划分页。

  386中,页的大小固定位4k字节,每一页的边界地址必须是4k的倍数。因此4g大小的地址空间被划分为1m个页,页的开始地址为xxxxx000h   因此,我们把页开始地址的高20位xxxxxh称为页码。线性地址空间的页码就是页开始边界线性地址的高20位,物理地址空间也是页开始边界物理地址的高20位。页码左移12位就是页的开始地址,所以页码规定了页 

  由于页大小固定为4k,且页的边界是4k的倍数,所以在把32位线性地址转换成32位物理地址的过程中,低12位地址保持不变。线性地址的低12位就是物理地址的低12位,假设分页机制将线性地址空间的xxxxxh页映射到物理地址空间的yyyyyh页,那么线性地址XXXXXxxxh被转换为YYYYYxxxh,因此,线性地址到物理地址的转换要解决的是线性地址空间的页到物理地址空间的页的映射,页就是线性地址高20位到物理地址高20位的转换。

二  线性地址到物理地址的转换

  1.映射表结构

    线性空间的页到物理空间的页之间的映射用表来描述。4g的地址空间划分为1m个页,因此需要1m个表项,若每个表项占用4个字节,那么该映射表就要占用4m个字节。为了避免映射表占用如此巨大的存储器资源,所以386将页映射表分为两级。

  第一级为页目录表   存储在一个4k字节物理页中。共有1k个表项,其中每个表项4字节长,包含对应第二级所在物理地址空间页的页码。

  第二级为页表,每张页表安排在一个4k字节的页中。每张页表都已1k个表项,每项4字节长,包含对应物理地址空间的页码。

  由于页目录表和页表均有1k个表项组成,所以使用10位的索引就能指定表项,即用10位的索引值乘以4加上基地址就得到了表项的物理地址。

  下图显示了由页目录表和页表构成的页映射表结构。从图中可见,cr3指定页目录表,页目录表可以指定1k个页表,这些页表可以分散存放在任意的物理页中,而不需要连续存放;每张页表可以指定1k个物理地址空间的页,这些物理地址空间的页可以任意地分散在物理地址空间中。需要注意的是,存储页目录表和页表的基地址是对齐在4k字节边界上的。

  

 

2.表项格式

  页目录表和页表中的表项都采用如下图所示的格式,从图中可见,最高20位(12-31)包含物理地址空间也得页码,就是高20位。低12位包含页的属性。p位表示该表项是否有效,1位有效。在通过页目录表和页表进行的线性地址到物理地址的转换过程中,无论在页目录表还是页表中遇到无效表项,都会引起页故障。

 

 

 

 p为0时   386不解释。

3.线性地址到物理地址的转换

  通过上述页目录表和页表实现32位线性地址到32位物理地址的转换。cr3的高20位作为页目录表所在物理页的页码。

  a  将线性地址的高10位作为目录表的索引,对应表项所包含的页码指定页表

  b  将线性地址空间的中间10位作为所指定的目录表中的页表项的索引,对应表项所包含的页码指定物理地址空间中的一页;

  c  将所指定的物理页的页码作为高20位,将线性地址的低12位不变作为32位物理地址的低12位。

   为了避免在每次存储器访问时都要访问内存中的页表,cpu将最近使用的线性-物理地址转换函数存储在cpu内部的页转换高速缓冲存储器中。在访问存储器页表之前总是先查阅高速缓存,仅当必须的转换不在高速缓存时,才访问存储器中的两级页表。页高速缓存页称为页转换查找缓存,记为TLB.

  在分页机制转换高速缓存中的数据与页表中数据的相关性,不是cpu进行维护,而由os保存。cpu不知道什么时候会修改页表,在一个合理的系统中,页表只能由os修改,操作系统可以直接地在软件修改页表后通过刷新高速缓存来保证相关性。高速缓存的刷新通过装入cpu的cr3来完成,实际上通过两条指令完成。

  mov   eax,cr3

  mov   cr3,eax

  一个修改页表项但是不用刷新高速缓存的情况是:修改不存在的表项的一部分,因为无效的表项不会存入高速缓存。当从磁盘上读入一页使其存在时,不必刷新高速缓存。

  在一个多cpu的系统中,通过使用不可分的读,写,修改周期,对于页表项的软件更新需要借助于LOCK前缀,从而保证修改页表的指令工作在不可分的读写修改周期中,

  在改变一个可能由另外的cpu使用的页表之前,最好使用一条加锁的AND指令在一个不分的操作中将p位清除为0.然后,该表项可根据要求进行修改,并随后把p位置成1而使表项称为可用。当修改页表项时必须及时通知(通常是中断的方式)系统中该表项已经被高速缓存的所有处理器刷新各自的页转换高速缓存,以撤销该表项的拷贝。在表项的旧拷贝被刷新之前,各cpu仍可继续访问旧的页,并可以设置正在被修改的表项的D位。如果这样做引起表项修改失败,则分页机制高速缓存最好标记为不存在之后,并在对表项进行另外的修改之前进行刷新。

4.不存在的页表

  存储全部1k张页表需要4m字节 (1个页表共1k个表项*4字节=4k,1k个表项*4k=4m字节),4k个字节用于存储页目录表,两级页表映射结构对于现行地址空间中不存在的或未使用的部分不必分配页表。除了给页目录表分配物理页外,仅当需要时才给页表分配物理页,所以页映射表的大小就对应于实际使用的线性地址空间的大小。

  页目录表项中的存在位p表明对应页表是否有效     有效则转换,p=0则无效不转换,会引起页故障。所以也目录表项中的属性位p使得os只需给覆盖实际使用的线性地址范围的页表分配物理页。

  页目录表项中的属性p位可用于把页表存储在虚拟存储器中,当发生由于所需页表无效而引起的页故障时,页故障处理程序再申请物理页,从磁盘上吧对应的页表读入,并把对应页目录表项中的p位置为1.即当需要时才位所需要的页表分配物理页。这样页表占用的物理页数量可以降到最小。

5.页的共享

  每个任务可以使用自己的页映射表独立的实现线性到物理地址的转换。但是如果使每个人物所用的页映射表具有部分相同的映射,那么也就可以实现部分页的共享。

  常用的实现页共享的方法是线性地址空间的共享,页就是不同任务的部分相同的线性地址空间的映射信息相同,具体表现为部分也表相同或页表内的部分页表项的页码相同。

  例如  a和b两个任务分别使用也目录表a和b    内的第00项的页码相同,就是页表0相同,那么a和b的00000000h到003fffffh的线性地址空间映射到相同的物理页。

  a和b使用的页表0不同,但是两张也表内的第0到第0ffh项的页码对应相同,那么a和b的00000000h到000fffffh线性地址空间映射到相同的物理页。

注意:共享的页表最好由两个也目录中同样的目录项所指定。因为他保证了在两个任务中同样的线性地址范围将映射到该全局区域。

三   页级保护和虚拟存储器的支持

 1.页级保护

  386不仅提供段保护,也提供页保护。分页机制只区分两种权级:0 1 2为系统权级,3为用户特权级。

  上图所示的页目录表和页表的表项中的保护属性位R/W和U/S就是用于对页进行保护

  表项的位1是读写属性位,记做R/W   指示表项所指定的页是否可读、写、或执行。

  如果为1   则可读写和执行

  如果为0则可读和执行,不能写

  上面的两条仅在cpu处于用户级时才能有效。当cpu处于系统级时总是可读可写可执行。

 

  表项的位2是用户/系统属性位,记做U/S.指示该表项所指定的页是否是用户级的页。

  如果为1   则为用户级的页,可由任何权级下执行的程序访问。

  为0则为系统级的页,仅仅由系统级下的执行程序访问。

         

 

上图可知,用户级的页有两种状态:读/执行      或读/写/执行  。系统级页对于系统级程序总是读写和执行,而对于用户级程序总是不可访问的。

与分段机制相同点:

  外层用户级执行的程序只能访问用户级的页,而内层系统级执行的程序,既可访问系统级页,页可访问用户级的页。

  不同点:

  内层系统级的执行程序,对任何页都有读,写,执行的访问权,即使规定为只允许读、执行的用户也,内层程序也有写的访问权。

  页目录表项中的保护属性位R/W和U/S对由该表项指定的页表所指定的全部1k各页起到保护作用。所以对页访问时引起的保护属性位R/W和U/S的值组合计算页目录表项和页表项中的保护属性位的值所得   如下图所示   组合为与操作

  

分页的保护机制在分段保护机制之后起作用。

先测试有关的段级保护,如果启用分页机制,那么在检查通过后,再测试页级保护。

如果段的类型为读/写,而页规定为只允许读/执行,那么不允许写

如果段的类型为读/执行,那么不论页如何保护,都不允许写

2.对虚拟存储器的支持

页表项中的p位时支持采用分页机制虚拟存储器的关键

p为1   表示表项指定的页位于物理存储器中,并且表项的高20位是物理页的页码

p为0  表示该线性地址空间中的页对应的物理地址空间中的页不在物理存储器中

如果程序访问不存在的页,会引起页异常,这样os可把该不存在的页从磁盘上读入,把所在物理页的页码填入对应表项并把表项中的p位置为1,然后使引起异常的程序恢复运行。

表项中访问位A和写标志位D   也用于支持有效地实现虚拟存储器

 表项的位5时访问属性位   A      在为了访问某存储单元而进行线性地址到物理地址的转换过程中,cpu总是把页目录表内的对应表项和其所指定页的对应表项中的A位置1,除非页表或页不存在,或者访问违反保护属性的规定。所以A=1表示已访问过对应的物理页。

  cpu不清除A位,通过周期性检测及清除A位,os就可确定哪些页在最近一段时间未被访问过,当资源紧缺时,这些最近未被访问的页很可能就被选择出来,将他们从内存换出到硬盘上去。

  表项位6是写标志位  D     在访问存储单元从线性到物理的转换过程中,如果是写访问并且可以写访问,cpu就把页表内对应表项中D位置1,但并不把页目录表内对应表项中的D位置1.

  当页从磁盘读入到内存时,页表中对应表项的D位被清0.所以D=1表示已写过对应的物理页。

  当页从内存换出到磁盘上时,如果该页的D位位1,那么必须进行写操作(把内存中的页写入磁盘时,cpu并不清除对应页表项的D位),但是如果要写到磁盘上的页的D位位0,那么不需要实际的磁盘写操作,只要简单第放弃内存中的该页即可,因为内存中的页与磁盘中的页具有完全先相同的内容。

四  页异常

    启用分页机制后  线性地址不再直接等于物理地址,在转换过程中出现如下的情况之一,会引起异常

  1.涉及的页目录表内的表项或也表内的表项中P=0   即该页不在内存

  2.发现试图违反页保护属性的规定而对页进行访问。

  报告页异常的中断向量号时14(0eh)   页异常属于故障类异常。进入故障处理程序时,保存的指针cs及eip指向发生故障的指令。排除故障后,iret   返回并重新执行产生故障的指令。

  当页故障发生时,cpu把引起页故障的线性地址装入cr2   页故障处理程序利用该线性地址确定对应的页目录表项和页表项

  页故障处理程序提供一个出错码,出错吗的格式16位  如下图

  

 

  U表示引起故障的权级,U=1表示用户权级为3   U=0表示系统级0 1 2.w表示访问类型,W=0表示读/执行,W=1表示写,P表示异常类型,P=0表示页不存在故障,P=1表示保护故障。页故障的相应处理模式同其他故障一样。

 

五 演示分页机制的实例   十    需要看杨季文的汇编教材

  

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

 

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

 

为了简单化,实例只有一个任务,并且没有局部描述符表和中断描述符表,不允许中断,也不考虑发生异常,甚至没有使用堆栈。实例执行步骤如下:

 

(1)在实模式下为进入保护模式作初始化;

 

(2)切换到保护模式后进入临时代码段,把部分演示代码传送到预定的内存,然后转演示代码段;

 

(3)建立页目录表;

 

(4)建立页表;

 

(5)启用分页管理机制;

 

(6)演示在分页管理机制启用后的程序执行和数据存取;

 

(7)关闭分页管理机制;

 

(8)退出保护模式,结束。

 

实例十源程序清单如下:

 

;名称:ASM10.ASM
;功能:演示使用分页管理机制
;编译:TASM ASM10.ASM
;连接:TLINK ASM10.OBJ
;============================================================================
INCLUDE         386SCD.INC
;============================================================================
PDT_AD          =       200000h ;页目录表所在物理页的地址
PT0_AD          =       202000h ;页表0所在物理页的地址
PT1_AD          =       201000h ;页表1所在物理页的地址
PhVB_AD         =       0b8000h ;物理视频缓冲区地址
LoVB_AD         =       0f0000h ;程序使用的逻辑视频缓冲区地址
MPVB_AD         =       301000h ;线性地址0B8000H所映射的物理地址
PhSC_AD         =       303000h ;部分演示代码所在内存的物理地址
LoSC_AD         =       402000h ;部分演示代码的逻辑地址
;============================================================================
GDTSeg          SEGMENT PARA USE16                ;全局描述符表数据段(16位)
;----------------------------------------------------------------------------
                ;全局描述符表GDT
GDT             LABEL   BYTE
                ;空描述符
DUMMY           Desc    <>
                ;规范段描述符及选择子
Normal          Desc    <0ffffh,,,ATDW,,>
Normal_Sel      =       Normal-GDT
                ;页目录表所在段描述符(在保护方式下初始化时用)及选择子
;页目录
PDT Desc <0fffh,PDT_AD AND 0ffffh,PDT_AD SHR 16,ATDW,,>  ;界限为4k,PDT_AD的物理地址与0ffffh相与 低16位不变,高位全为0成为基地址的低16位;
                      ;右移16位变为基地址的中8位
PDT_Sel = PDT-GDT
======
;页表 ;页表0所在段描述符(在保护方式下初始化时用)及选择子 PT0 Desc <0fffh,PT0_AD AND 0ffffh,PT0_AD SHR 16,ATDW,,>  ;同上 先设置段的描述符 确定段基地址 同时确定页表物理地址 PT0_Sel = PT0-GDT ;页表1所在段描述符(在保护方式下初始化时用)及选择子 PT1 Desc <0fffh,PT1_AD AND 0ffffh,PT1_AD SHR 16,ATDW,,>  ;同上 PT1_Sel = PT1-GDT
========= ;逻辑视频缓冲区段描述符及选择子 LoVideo Desc <3999,LoVB_AD AND 0ffffh,LoVB_AD SHR 16,ATDW,,>  ;逻辑地址(虚拟地址),确定段的基地址 是物理地址吗? LoVideo_Sel = LoVideo-GDT ;逻辑上的部分演示代码段的描述符及选择子 LoCode Desc <SCodeLen-1,LoSC_AD AND 0ffffh,LoSC_AD SHR 16,ATCE,,>  ;逻辑地址(虚拟地址),确定段描述符的基地址 LoCode_Sel = LoCode-GDT
========= ;预定内存区域(用于部分演示代码)的段描述符及选择子 TPSCode Desc <SCodeLen-1,PhSC_AD AND 0ffffh,PhSC_AD SHR 16,ATDW,,>  ;确定物理地址 TPSCode_Sel = TPSCode-GDT ;---------------------------------------------------------------------------- ;以下是需额外初始化的描述符                需要初始化的gdt表项 EFFGDT LABEL BYTE ;临时代码段描述符及选择子 TempCode Desc <0ffffh,TempCodeSeg,,ATCE,,> TempCode_Sel = TempCode-GDT ;演示代码段描述符及选择子 DemoCode Desc <DemoCodeLen-1,DemoCodeSeg,,ATCE,,> DemoCode_Sel = DemoCode-GDT ;演示任务数据段描述符及选择子 DemoData Desc <DemoDataLen-1,DemoDataSeg,,ATDW,,> DemoData_Sel = DemoData-GDT ;初始化时要移动的代码段描述符及选择子(移动时作为数据对待) SCode Desc <SCodeLen-1,SCodeSeg,,ATDR,,> SCode_Sel = SCode-GDT ;---------------------------------------------------------------------------- GDTLen = $-GDT ;全局描述符表长度 GDNum = ($-EFFGDT)/(SIZE Desc) ;需特殊处理的描述符数 ;---------------------------------------------------------------------------- GDTSeg ENDS ;全局描述符表段定义结束 ;============================================================================ ;这部分代码在初始化时被复制到预定的内存区域,其功能是在屏幕上显示提示信息 ;---------------------------------------------------------------------------- SCodeSeg SEGMENT PARA USE16 ASSUME CS:SCodeSeg,DS:DemoDataSeg ;---------------------------------------------------------------------------- SBegin PROC FAR mov ax,LoVideo_Sel ;指向逻辑视频缓冲区段 mov es,ax mov di,0 mov ah,17h       ;??? 显示块结束符 查询ascii码表 mov cx,MessLen      ;计数为 字符串长度 字节长度 S1: lodsb            ;将es:di指向的字节传送到al stosw            ;写字 ax--》es:di 其中ah恒为17h 区块传输结束对应ascii码表 loop S1 JUMP16 DemoCode_Sel,Demo3 ;跳转到演示代码段的demo3的位置 SBegin ENDP ;---------------------------------------------------------------------------- MLen = $-SBegin SCodeLen = $ SCodeSeg ENDS ;============================================================================ DemoDataSeg SEGMENT PARA USE16 ;演示任务数据段 Mess DB 'Page is OK!' MessLen = $-Mess DemoDataLen = $ DemoDataSeg ENDS ;============================================================================ DemoCodeSeg SEGMENT PARA USE16 ;演示任务代码段 ASSUME CS:DemoCodeSeg ;---------------------------------------------------------------------------- DemoBegin PROC FAR mov ax,PDT_Sel        ;页目录表所在段 初始化页目录表项 mov es,ax xor di,di          ;di清零 mov cx,1024           xor eax,eax ;先把全部表项置成无效 rep stosd ;再置表项0和表项1 双字传送 4个字节传送 借助eax ---》es:di 从寄存器到存储单元 mov DWORD PTR es:[0],PT0_AD OR (USU+RWW+PL)  ;初始化页目录表项中的第0项 mov DWORD PTR es:[4],PT1_AD OR (USU+RWW+PL)  ;初始化页目录表项中的第1项 mov ax,PT0_Sel ;初始化页表0   mov es,ax xor di,di            ;指向段的基地址 mov cx,1024 xor eax,eax            ;eax清0 or eax,USU+RWW+PL        ; Demo1: stosd                  ;eax--》es:di   add eax,1000h ;先全部置成对应等地址的 最低12位保持不变 1000h 2000h 3000h... 确定1024个页表项的页码 loop Demo1 ;物理页,再特别设置两个表项 mov di,(PhVB_AD SHR 12)*4          ; 看下方的说明7和8 mov DWORD PTR es:[di],MPVB_AD+USS+RWW+PL  ; mov di,(LoVB_AD SHR 12)*4        ; mov DWORD PTR es:[di],PhVB_AD+USU+RWR+PL  ;

mov ax,PT1_Sel ;初始化页表1 mov es,ax xor di,di mov cx,1024 mov eax,400000h         ;eax为400000h的原因是什么? Demo2: stosd ;先把全部表项设置为无效 add eax,1000h loop Demo2 ;再特别设置1项 mov di,((LoSC_AD SHR 12)AND 3ffh)*4  ;      说明9 mov DWORD PTR es:[di],PhSC_AD+USU+RWR+PL  ;
mov eax,PDT_AD        ;页目录表所在的物理地址 送入eax寄存器 mov cr3,eax          ;将也目录表中的物理地址送入cr3控制寄存器 mov eax,cr0          ;开启分页模式 cr0中的最高位为1 or eax,80000000h      ;确保eax的最高位为1 mov cr0,eax         jmp SHORT PageE     PageE: mov ax,DemoData_Sel        ;演示任务数据段 显示其中的字符 mov ds,ax               mov si,OFFSET Mess          ;si指向数据段中的mess的偏移位置 JUMP16 LoCode_Sel,SBegin        ;跳转到部分演示代码段 sbegin位置 scode段被复制到指定的内存区
          ;locode_sel指向locode逻辑演示代码段 通过段机制和页机制转换,实际的地址为phscode的物理地址
          ;对应关系为 从402000h页机制转换到303000h 执行的是303000h处的指令
          ;tpscode 段描述符中 表明 此段的基地址正好是phsc_ad 所以此位置的指令就是初始化时移动过来的SCodeSEG中的代码 Demo3: mov eax,cr0 and eax,7fffffffh     ;关闭分页机制 cr0 最高位置为0 mov cr0,eax jmp SHORT PageD PageD: mov ax,Normal_Sel        ;将所有寄存器指向规范的选择子 JUMP16 TempCode_Sel,ToDOS      宏跳转演示代码中的TODOS位置 DemoBegin ENDP ;---------------------------------------------------------------------------- DemoCodeLen = $ DemoCodeSeg ENDS ;============================================================================ TempCodeSeg SEGMENT PARA USE16 ;临时任务的代码段 ASSUME CS:TempCodeSeg ;---------------------------------------------------------------------------- Virtual PROC FAR cld ;为演示在启用分页机制后执 mov ax,SCode_Sel ;行位于较高线性地址空间中 mov ds,ax ;的代码作准备 mov ax,TPSCode_Sel mov es,ax mov si,OFFSET SBegin mov di,si          ;相对于基地址的偏移地址送给di mov cx,MLen ;把分页演示代码复制到预定 rep movsb ;内存 字节传送 JUMP16 DemoCode_Sel,DemoBegin    ;跳转到演示代码段 ToDOS: mov ds,ax            ;返回实模式前的准备 ds es 指向规范选择子 mov es,ax mov eax,cr0 ;准备返回实模式 and al,11111110b        ;最低位设置为0 mov cr0,eax JUMP16 <SEG Real>,<OFFSET Real> Virtual ENDP ;---------------------------------------------------------------------------- TempCodeSeg ENDS ;============================================================================ RCodeSeg SEGMENT PARA USE16 ;实方式的初始化代码和数据 ASSUME CS:RCodeSeg,DS:RCodeSeg ;---------------------------------------------------------------------------- VGDTR PDesc <GDTLen-1,> ;---------------------------------------------------------------------------- Start PROC                  ;主程序入口地址 push cs pop ds cld call InitGDT ;初始化全局描述符表GDT EnableA20 lgdt QWORD PTR VGDTR ;装载GDTR cli ;关中断 mov eax,cr0 or al,1 mov cr0,eax JUMP16 <TempCode_Sel>,<OFFSET Virtual> Real: DisableA20        ;关闭a20地址线 sti            ;开中断 mov ax,4c00h int 21h Start ENDP           ;  最终结束 ;----------------------------------------------------------------------------



InitGDT PROC push ds mov ax,GDTSeg mov ds,ax mov cx,GDNum mov si,OFFSET EFFGDT InitG: mov ax,[si].BaseL movzx eax,ax shl eax,4 shld edx,eax,16 mov WORD PTR [si].BaseL,ax mov BYTE PTR [si].BaseM,dl mov BYTE PTR [si].BaseH,dh add si,SIZE Desc loop InitG pop ds mov bx,16 mov ax,GDTSeg mul bx mov WORD PTR VGDTR.Base,ax mov WORD PTR VGDTR.Base+2,dx ret InitGDT ENDP ;---------------------------------------------------------------------------- RCodeSeg ENDS END Start

 

 程序解读说明

1.部分演示代码的移动

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

2.页映射表的初始化

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

页表0把线性地址空间中的00000000h-003fffffh映射到物理地址空间中。实例在初始化页表0时,使该线性地址空间直接映射到相同地址的物理地址空间,除线性地址空间页表1把线性地址空间中页码为00b8h和000f0h这两项意外。000b8h页被映射到页码为00301h的物理页,而000f0h页被映射到页码为000b8h的物理页。

页表1把线性地址空间中的00400000h--007fffffh映射到物理地址空间中。实例在初始化页表1时,似乎使该线性地址空间直接映射到相同地址的物理地址空间,但是处理对应线性地址空间中00402h的表项被另外设置外,其它表项中的p位为0,也即表示对应物理页不存在。初始化后,页表1的第2项把线性地址空间中的00402h页映射到页码为00303h的物理页,也就是存放部分演示代码的指定内存区域。

3.启用分页管理机制

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

  mov   eax,cr0

  or  eax,80000000h

  mov  cr0,eax

  jmp  SHORT  PageE

 PageE:  ...

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

4.关闭分页管理机制

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

5.地址转换的演示

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

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

6.页级保护的说明

 在进入保护模式之后,权级一直是0,所以,无论系统级和用户级的页,无论只读/执行,还是读写执行,总是可以进行各种形式的访问。

 7.说明   mov di,(PhVB_AD SHR 12)*4     此时页表0的物理地址为202000h    页表0中的第0项的内容为高20位的内容为00000h,第1项为00001h,顺延全部设置成对应等地址的物理页,然后设置第0b8h索引项的内容为mpvb_AD的物理地址  即000b8000h线性地址对应的物理地址为00301000h,设置di的值为上面的式子是因为将phvb_ad的值0b8000h右移12位正好是线性地址000b8000线性地址中的中间十位作为页表0中的索引值0b8h *4正好是di指向的页表0所在页的0b8索引指向字节单元的指针,下面的指令

 mov     DWORD PTR es:[di],MPVB_AD+USS+RWW+PL 将上面中的0b8000h的线性地址对应的物理地址00301000h加上权限位变为实际的物理地址
存入di指向的内存单元 变为页表0中的第0b8h索引指向的页表0项的实际内容。
虚拟地址到物理地址即 0b8000h到mpvb_ad地址的转换过程
8.
          mov di,(LoVB_AD SHR 12)*4
                mov     DWORD PTR es:[di],PhVB_AD+USU+RWR+PL
       程序使用的逻辑视频缓冲区地址   
      LOVB_AD右移12位 作为页表0中的索引为0f0h指向的表项,
  同上乘4的到di指向的页表0中的字节单元 相对于页表0的基地址
  将phvb_ad 实际的物理地址加上权限 送入es:di指向的内存空间 共32位4字节
实际上就是将程序使用的逻辑地址转换成物理地址的一个过程
LoVB_AD到phvb_ad    逻辑到物理   
9.
  mov     di,((LoSC_AD SHR 12)AND 3ffh)*4  
  mov     DWORD PTR es:[di],PhSC_AD+USU+RWR+PL 
losc_ad  右移12位   得到402 为索引  超出了索引的最大值,所以需要去掉高位,保留低10位获取真正的索引值 
即与3ffh做与操作。
部分演示代码的物理地址填充到di指向的内存单元
功能为将部分演示代码的逻辑地址转换为物理地址 即losc_ad变为phsc_ad










 

  

 

posted on 2017-11-23 14:44  学习记录园  阅读(2341)  评论(1编辑  收藏  举报