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

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

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

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

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

    为了简单化,实例只有一个任务,并且没有局部描述符表和中断描述符表,不允许中断,也不考虑发生异常,甚至没有使用堆栈。实例执行步骤如下: 
    (1)在实模式下为进入保护模式作初始化;
    (2)切换到保护模式后进入临时代码段,把 部分演示代码传送到预定的内存,然后转演示代码段;
    (3)建立页目录表;
    (4)建立页表;
    (5)启用分页管理机制;
    (6)演示在分页管理机制启用后的程序执行和数据存取;
    (7)关闭分页管理机制;
    (8)退出保护模式,结束。
.386P
;----------------------------------------------------------------------------
;打开A20地址线
;----------------------------------------------------------------------------
EnableA20 MACRO
	push ax
	in al,92h
	or al,00000010b
	out 92h,al
	pop ax
ENDM
;----------------------------------------------------------------------------
;关闭A20地址线
;----------------------------------------------------------------------------
DisableA20 MACRO
	push ax
	in al,92h
	and al,11111101b
	out 92h,al
	pop ax
ENDM
;----------------------------------------------------------------------------
;16位偏移的段间直接转移指令的宏定义(在16位代码段中使用)
;----------------------------------------------------------------------------
JUMP16 MACRO Selector,Offset
	DB 0eah ;操作码
	DW Offset ;16位偏移量
	DW Selector ;段值或段选择子
ENDM
;----------------------------------------------------------------------------
;存储段描述符结构类型定义
;----------------------------------------------------------------------------
Desc STRUC
	LimitL DW 0 ;段界限(BIT0-15)
	BaseL DW 0 ;段基地址(BIT0-15)
	BaseM DB 0 ;段基地址(BIT16-23)
	Attributes DB 0 ;段属性
	LimitH DB 0 ;段界限(BIT16-19)(含段属性的高4位)
	BaseH DB 0 ;段基地址(BIT24-31)
Desc ENDS
;----------------------------------------------------------------------------
;伪描述符结构类型定义(用于装入全局或中断描述符表寄存器)
;----------------------------------------------------------------------------
PDesc STRUC
	Limit DW 0 ;16位界限
	Base DD 0 ;32位基地址
PDesc ENDS
;----------------------------------------------------------------------------
;存储段描述符类型值说明
;----------------------------------------------------------------------------
ATDR EQU 90h ;存在的只读数据段类型值
ATDW EQU 92h ;存在的可读写数据段属性值
ATDWA EQU 93h ;存在的已访问可读写数据段类型值
ATCE EQU 98h ;存在的只执行代码段属性值
;----------------------------------------------------------------------------



;----------------------------------------------------------------------------
;DPL值说明
;----------------------------------------------------------------------------
DPL0 EQU 00h ;DPL=0
;----------------------------------------------------------------------------

;----------------------------------------------------------------------------

;----------------------------------------------------------------------------
;分页机制使用的常量说明
;----------------------------------------------------------------------------
PL EQU 1 ;页存在属性位
RWR EQU 0 ;R/W属性位值,读/执行
RWW EQU 2 ;R/W属性位值,读/写/执行
USS EQU 0 ;U/S属性位值,系统级
USU EQU 4 ;U/S属性位值,用户级
;----------------------------------------------------------------------------


;============================================================================
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,,>
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
;----------------------------------------------------------------------------
;以下是需额外初始化的描述符
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
	mov     cx,MessLen
S1:    lodsb
	stosw
	loop    S1
	JUMP16  DemoCode_Sel,Demo3
SBegin          ENDP
;----------------------------------------------------------------------------
MLen            =       $-SBegin
SCodeLen        =       $-SCodeSeg
SCodeSeg        ENDS
;============================================================================
DemoDataSeg     SEGMENT PARA USE16                ;演示任务数据段
Mess            DB      'Page is OK!'
MessLen         =       $-Mess
DemoDataLen     =       $-DemoDataSeg
DemoDataSeg     ENDS
;============================================================================
DemoCodeSeg     SEGMENT PARA USE16                ;演示任务代码段
ASSUME  CS:DemoCodeSeg
;----------------------------------------------------------------------------
DemoBegin       PROC    FAR
	mov     ax,PDT_Sel
	mov     es,ax
	xor     di,di
	mov     cx,1024
	xor     eax,eax                   ;先把全部表项置成无效
	rep     stosd                     ;再置表项0和表项1
	mov     DWORD PTR es:[0],PT0_AD OR (USU+RWW+PL)
	mov     DWORD PTR es:[4],PT1_AD OR (USU+RWW+PL)
	mov     ax,PT0_Sel                ;初始化页表0
	mov     es,ax
	xor     di,di
	mov     cx,1024
	xor     eax,eax
	or      eax,USU+RWW+PL
Demo1: stosd
	add     eax,1000h                 ;先全部置成对应等地址的 
	loop    Demo1                     ;物理页,再特别设置两个表项 1000h=4096
	mov     di,(PhVB_AD SHR 12)*4
	mov     DWORD PTR es:[di],MPVB_AD or (USS+RWW+PL)
	mov     di,(LoVB_AD SHR 12)*4
	mov     DWORD PTR es:[di],PhVB_AD or (USU+RWR+PL)
	mov     ax,PT1_Sel                ;初始化页表1
	mov     es,ax
	xor     di,di
	mov     cx,1024
	mov     eax,400000h
Demo2: stosd                             ;先把全部表项设置为无效
	add     eax,1000h
	loop    Demo2                     ;再特别设置1项
	mov     di,((LoSC_AD SHR 12)AND 3ffh)*4
	mov     DWORD PTR es:[di],PhSC_AD or (USU+RWR+PL)
	mov     eax,PDT_AD
	mov     cr3,eax
	mov     eax,cr0
	or      eax,80000000h
	mov     cr0,eax
	jmp     SHORT PageE
PageE:  mov     ax,DemoData_Sel
	mov     ds,ax
	mov     si,OFFSET Mess
	JUMP16  LoCode_Sel,SBegin
Demo3:  mov     eax,cr0
	and     eax,7fffffffh             ;关闭分页机制
	mov     cr0,eax
	jmp     SHORT PageD
PageD:  mov     ax,Normal_Sel
	JUMP16  TempCode_Sel,ToDOS
DemoBegin       ENDP
;----------------------------------------------------------------------------
DemoCodeLen     =       $-DemoCodeSeg
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
	mov     cx,MLen                   ;把分页演示代码复制到预定
	rep     movsb                     ;内存
	JUMP16  DemoCode_Sel,DemoBegin
ToDOS:  mov     ds,ax
	mov     es,ax
	mov     eax,cr0                   ;准备返回实模式
	and     al,11111110b
	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    FWORD PTR VGDTR           ;装载GDTR
	cli                               ;关中断
	mov     eax,cr0
	or      al,1
	mov     cr0,eax
	JUMP16  <TempCode_Sel>,<OFFSET Virtual>
Real:  DisableA20
	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 时,使该线性地址空间直接映射到相同地址的物理地址空间,除线性地址空间中页码为 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)。对于图,自己最好把数据填上去,这样更直观,这里为了方便,我没有填上数据。

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
	mov     cx,MLen                   ;把分页演示代码复制到预定
	rep     movsb                     ;内存
	JUMP16  DemoCode_Sel,DemoBegin

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

	mov     eax,PDT_AD
	mov     cr3,eax
	mov     eax,cr0
	or      eax,80000000h
	mov     cr0,eax
	jmp     SHORT PageE
PageE:  mov     ax,DemoData_Sel
	mov     ds,ax
	mov     si,OFFSET Mess
	JUMP16  LoCode_Sel,SBegin

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

	mov     di,(PhVB_AD SHR 12)*4
	mov     DWORD PTR es:[di],MPVB_AD or (USS+RWW+PL)
	mov     di,(LoVB_AD SHR 12)*4
	mov     DWORD PTR es:[di],PhVB_AD or (USU+RWR+PL)

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

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

	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,所以,无论系统级和用户级页,无论只能读/执行,还是读/执行/写,总是可进行各种形式

的访问。 


程序运行结果展示:





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值