为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

内存管理与保护模式

2017-10-26 50页 doc 594KB 26阅读

用户头像

is_079973

暂无简介

举报
内存管理与保护模式内存管理与保护模式 本实验讨论x86采用的内存管理模式:从分段实模式到分段保护模式,再到分页虚拟内存管理,并介绍进入保护模式、启动分页机制、以及获取内存大小的基本方法。 1 x86的内存管理 内存的分段(segmentation)管理符合程序的逻辑结构,利于程序的保护和动态控制。分页(paging)则最适合虚拟内存的管理需要。目前主流操作系统的内存管理采用的是分页方法(如类Unix),也有采用段页式的(如Wiondows)。 Intel 8086支持不带保护功能的分段内存管理,80286开始引入带保护功能的分段内存管理...
内存管理与保护模式
内存管理与保护模式 本实验讨论x86采用的内存管理模式:从分段实模式到分段保护模式,再到分页虚拟内存管理,并介绍进入保护模式、启动分页机制、以及获取内存大小的基本方法。 1 x86的内存管理 内存的分段(segmentation)管理符合程序的逻辑结构,利于程序的保护和动态控制。分页(paging)则最适合虚拟内存的管理需要。目前主流操作系统的内存管理采用的是分页方法(如类Unix),也有采用段页式的(如Wiondows)。 Intel 8086支持不带保护功能的分段内存管理,80286开始引入带保护功能的分段内存管理,80386又引入了支持虚拟内存的分页内存管理,但其分页是建立在分段基础上的。IA-32和x64处理器,都支持段页式内存管理。 1.1 实模式 Intel的16位处理器8086,采用的是分段内存管理,CPU中有CS、DS、SS和ES四个16位段寄存器,作为基地址,分别用于生成代码、数据、堆栈和其他段的物理地址: 20位物理地址 = 16位段寄存器值*16(或左移4位) + 16位偏移量 2016寻址空间只有(2=)1MB,最大段长为(2=)64KB。 因为这样的CPU只能生成和访问真实的物理内存地址,所以被称为实地址模式(real-address mode),简称为实模式(real mode)。 不过这种分段方法并不是现代的分段技术,没有提供任何内存保护功能,不能阻止内存的越界访问。而且8086 CPU也没有提供任何特权分级,谁都可以任意改变CS、DS和SS寄存器中的值,从而可以执行/访问内存任何地址的指令/数据,完全没有安全可言。因此在8086 CPU上,是不可能构建现代操作系统的。 1.2 保护模式 1982年推出的16位的80286处理器,在x86体系中首次引入了(分段式的)内存保护机制,称之为保护模式(protected mode)。不过80286的保护模式只支持24位的地址空间 1616+824(最多只能访问16MB内存)和16位的界限大小(最大段长为2=64KB或2=2=16MB),且只能从实模式进入保护模式,而不能从保护模式返回实模式。 1985年推出的32位的80386处理器,其保护模式支持32位的地址空间,寻址空间达 32到(2=)4GB。但是为了与分段式的8086兼容,80386并没有使用简单的线性地址空间,而是采用了基于分段的分页内存管理方法(其中的分页管理是80386新增加的)。这里的分段是必须的,分页则是可选的。 1(分段 分段将处理器可寻址的内存空间(称为线性地址空间)划分为较小的受保护的地址空间(称为段),段可用于容纳程序的代码、数据和堆栈,或系统的数据结构(如TSS或LDT)。可以为每个运行的程序指派各自的段集。分段机制还允许将段分类,从而可限制在某个特定段类上能够执行的操作。 为了定位特定段中的字节,必须提供一个逻辑地址(也称为远指针),它包含一个段选择符和一个偏移量。段选择符是段唯一的标识符,它提供段描述符在描述符中的偏移量。段描述符指定段的大小、访问权限和特权级、段类型、段在线性地址空间中的起始地址(基址)。段的基址+偏移量=处理器线性地址空间中的线性地址。 2(分页 如果不使用分页,则IA-32处理器的线性地址空间直接映射到处理器的物理地址空间,不支持虚拟内存管理。对IA-32处理器,只有分页机制才支持虚拟内存,使处理器的线性地址空间可以大于内存的物理地址空间。 但是IA-32处理器的分页是建立在分段的基础之上的,即IA-32处理器采用的是段页式 内存管理方法。在使用分页管理时,每个段被分成若干等长的页(对IA-32处理器,典型的页帧大小是4KB),这些页可以存放在内存中,也可以存放在磁盘上(即支持虚拟内存)。 当程序或任务试图访问线性地址空间中的地址位置时,处理器利用页目录和页表,将线性地址转换为物理地址。 为了节省内存空间,IA-32处理器采用的是两级分页方式。如果采用单级分页,对4KB 1220(=2)页帧大小和4GB地址空间,需要1M(=2)个表项,每个表项4B则整个页表就需要4MB空间。由于一般的进程并不需要使用整个4GB的空间,所以大多数表项都空着的, 10非常浪费。如果采用二级分页,一级为页目录(Page Directory,PD),有(2=)1024个页目录项(Page Directory Entry,PDE)。每个页目录项指向一个二级页表(Page Table,PT), 1020它也有(2=)1024个页表项(Page Table Entry,PTE),总共也是(1024*1024=2=)1M个页。但对空页目录项,不必创建其对应的页表,可以节省空间。 31 22 21 12 11 0 页目录项序号dir 页表项序号table 页帧内偏移量offset 4KB分页时的线性地址格式 31 12 11 10 9 8 7 6 5 4 3 2 1 0 I P P U R P 页表起始物理地址的高20位 Ignored g A C W / / P S n D T S W 4KB分页时的PDE(页目录项)格式 其中: , P = Present存在(=0:不在内存中,其余各位忽略、=1:在内存中,其余各位有意义) , R/W = Read / Write读/写(=0:只读、=1:可写) , U/S = User / Supervisor用户/超级管理员(=0:系统权限、=1:用户权限) , PWT = Page-level Write-Through页级直接写 , PCD = Page-level Cache Disable页级禁用缓存 , A = Accessed访问过(0:未访问过、1:已访问过) , Ign = Ignored被忽略 , PS = Page Size页大小(0:4KB、1:4MB) , Ignored被忽略 31 12 11 10 9 8 7 6 5 4 3 2 1 0 P P P U R 页帧起始物理地址的高20位 Ignored G A D A C W / / P T D T S W 4KB分页时的PTE(页表项)格式 其中: , D = Dirty脏(即对应页被软件改写过)(0:未脏、1:已脏) , PAT = 用于确定内存类型 , G = Global全局(0:局部、1:全局) 如果采用4MB页帧大小,则只需要单级分页(只需要页目录)即可。 31 22 21 0 页目录dir 偏移量offset 4MB分页时的线性地址格式 分段分页 逻辑地址,线性地址,物理地址 映射映射 2 IA-32的寻址方式 分段不分页 逻辑地址,线性地址,物理地址映射 IA-32的分段保护模式下的逻辑地址由一个16位的段选择符和一个32位的偏移量构成。 2.1 段选择符 80386的8个通用寄存器和指令指针寄存器及标志寄存器都扩展成了32位(如EAX、ESP、EIP、EFLAGS,对应寄存器名前增加了一个字母E,代表Extended扩展),但是仍然保留原来的段寄存器为16位不变,只是另外增加了两个新的16位辅助段寄存器FS和GS(字母F和G没有特殊的含义,只是跟随在已有段寄存器字母C[S]、D[S]、E[S]之后的两个英文字母而已),一共有6个段寄存器:代码段的CS、数据段的DS、堆栈段的SS和3个辅助段寄存器——ES、FS和GS。 80386的主要寄存器 但是在32位的保护模式下,这些16位的段寄存器的,不再是实模式下(最大64KB段长的)段基址,而是一种包含地址描述符结构数组(段表)的索引(index,下标)和特权级(privilege level)等设置的一种数据结构——段选择符(segment selector)。 这里的索引(偏移值)指向全局或局部段描述符表(段表)中的一个(描述符)表项,因索引/偏移值为16位,而每个段描述符占8B,所以该偏移值的低3位必须为0(另外用作 1613TI和RPL),只有段寄存器的高13位才有效,因此段描述符表中最多可有(2/8=2=)8192(=8K)个表项。 其实在IA-32处理器中,除了6个可见的16位段寄存器外,还有隐藏不可见的与每个段寄存器对应的(64位)影子结构,用于实现分段管理中的保护功能。 2.2 段描述符 段选择符指向段描述符表(segment descriptor table)中的一个段描述符表项,段描述符用于分段内存管理中的地址生成和保护。在段描述符(表项)中,包含32位段基址、20位段界限和12个控制位,共计64位(8B)。为了与80286(48位[6B]的)段描述符中的24位基址和16位界限兼容,所以80386段描述符中的32位基址和20位界限在8字节的结构中并不是连续的(高8位基址和高4位界限被放在了新增加的2个高位字节中)。 B31~B24 G D/B 0 AVL L19~L16 3 P DPL S Type B23~B16 2 B15~B0 1 L15~L0 0 15 0 描述符 其中: , L = limit界限(20位段的界限) , B = base基址(32位段的基地址) , Type(类型)占4个二进制位,从低到高依次为(对代码段或数据段描述符): , A = access访问(=0:未被访问、=1:已被访问) , RW = read/write读/写[数据段](=0:只读不可写[数据段]/只执行不可读[代码段]、 =1:可读写[数据段]/可执行和读[代码段]) , ED/C = extend direction/conforming伸展方向/一致(=0:向上[高地址]伸展[数据段]/ 非一致[代码段]、1:向下[低地址]伸展[堆栈段]/一致 [代码段]) , E = code代码(0:数据段[包括堆栈]、1:代码段) , S = descriptor type描述符类型(=0:系统描述符、=1:代码段或数据段描述符) , DPL = descriptor privilege level描述符特权级(0~3:本段的特权级为0~3) , P = present存在(0:段不存在,描述符无意义、1:段存在,描述符含有效基址和界限) , AVL = available for System系统可用(始终设为0) , D/B = default operation size默认操作大小(0:16位段、1:32位段) 12, G = Granularity 粒度/段长的单位(=0:字节、=1:4KB=2B) 下面是全局描述符的结构定义: struct gdt_entry { unsigned short limit_low; unsigned short base_low; unsigned char base_middle; unsigned char access; unsigned char granularity; unsigned char base_high; } __attribute__((packed)); 因为段描述符中的控制位G可以指定段长的单位,G=0时单位为字节,G=1时的单位 122020+12 为4KB=2B。所以段描述符中20位段界限,最大(段长)可以是(2=)1MB或(2= 32 2=)4GB。(使用x86处理器的)Linux采用的是分页内存管理,不支持内存的分段管理,就是利用粒度为4KB的段界限,将32位处理器所支持的整个4GB内存分为了一个段,绕开了x86默认的分段管理机制。 如果描述符中的S控制位=0,则为系统描述符,包括如下两类: , 系统段描述符 , LDT(Local Descriptor-Table,局部描述符表)段描述符 , TSS(Task-State Segment,任务状态段)描述符 , 门描述符 , 调用门(call-gate)描述符 , 中断门(interrupt-gate)描述符 , 陷阱门(trap-gate)描述符 , 任务门(task-gate)描述符 系统描述符类型 Type字段 描述 数值 11 10 9 8 0 0 0 0 0 保留 1 0 0 0 1 16位TSS(可用) 2 0 0 1 0 LDT 3 0 0 1 1 16位TSS(忙) 4 0 1 0 0 16位调用门 5 0 1 0 1 任务门 6 0 1 1 0 16位中断门 7 0 1 1 1 16位陷阱门 8 1 0 0 0 保留 9 1 0 0 1 32位TSS(可用) 10 1 0 1 0 保留 11 1 0 1 1 32位TSS(忙) 12 1 1 0 0 32位调用门 13 1 1 0 1 保留 14 1 1 1 0 32位中断门 15 1 1 1 1 32位陷阱门 2.3 描述符表 , GDT(Global Descriptor Table,全局描述符表)——是线性空间里的一种数据结构(本身不是一个段),每个系统都必须定义唯一一个GDT,可被系统中的所有程序和任务使用。GDT的基线性地址(8B对齐)和界限必须(使用LGDT指令)装入GDTR寄存器。GDT的界限值为字节数。 , LDT(Local Descriptor Table,局部描述符表)——本身是一个段,可定义若干个,可被多个任务共享。LDT位于LDT类型的系统段,GDT中必须包含一个LDT的段描述符。LDT使用其段选择符访问。为了在访问LDT时消除地址转换,LDT的段选择符、基线性地址、界限、访问权限被存储在LDTR寄存器中。 IDTR = Interrupt Descriptor Table Register,中断描述符表寄存器 3 保护模式 IA-32架构的CPU支持4种操作模式: , 保护模式(protected mode)——这是处理器的天然态(native state)。 , 虚拟8086模式(virtual-8086 mode)——不是一种实际的处理器模式,只是指在保 护模式下可以直接执行实模式8086软件的能力。 , 实地址模式(Real-address mode)——实现8086处理器的编程环境。在加电或重 启时,处理器被置于实地址模式。 , SMM(System Management Mode,系统管理模式)——为操作系统或执行程序提 供一种实现平台特定功能(如电源管理和系统安全)的透明机制。 3.1 进入保护模式 1(进入保护模式的主要步骤 , 准备GDT , 准备GDTR指针(48位GDT参数:16位界限与32位基址) , 用LGDT指令将GDT参数加载到寄存器GDTR , 关闭中断 , 打开A20地址线 , 置CR0寄存器的PE位 , 跳转进入保护模式 2(常量与宏 , NASM宏的定义与使用 , 宏的定义格式: %macro 宏名 参数个数 „„ ; (含诸参数的)指令序列,在其中用%i来表示参数i %endmacro , 宏的使用方法: 宏名 参数1, 参数2, „„ , (段)描述符格式: B31~B24 G D/B 0 AVL L19~L16 3 P DPL S Type B23~B16 2 B15~B0 1 L15~L0 0 15 0 描述符格式 D/B=1:32位段 DPL=0~3:描述符特权级 , 描述符类型常量与用于生成描述符(和门)宏的包含文件代码(pm.inc) ;---------------------------------------------------------------------------- ; 在下列类型值命名中: ; DA_ : Descriptor Attribute 描述符属性 ; D : 数据段 ; C : 代码段 ; S : 系统段 ; R : 只读 ; RW : 读写 ; A : 已访问 ; 其它 : 可按照字面意思理解 ;---------------------------------------------------------------------------- ; 描述符类型 DA_32 EQU 4000h ; 32 位段 DA_DPL0 EQU 00h ; DPL = 0 DA_DPL1 EQU 20h ; DPL = 1 DA_DPL2 EQU 40h ; DPL = 2 DA_DPL3 EQU 60h ; DPL = 3 ; 存储段描述符类型 DA_DR EQU 90h ; 存在的只读数据段类型值 DA_DRW EQU 92h ; 存在的可读写数据段属性值 DA_DRWA EQU 93h ; 存在的已访问可读写数据段类型值 DA_C EQU 98h ; 存在的只执行代码段属性值 DA_CR EQU 9Ah ; 存在的可执行可读代码段属性值 DA_CCO EQU 9Ch ; 存在的只执行一致代码段属性值 DA_CCOR EQU 9Eh ; 存在的可执行可读一致代码段属性值 ; 系统段描述符类型 DA_LDT EQU 82h ; 局部描述符表段类型值 DA_TaskGate EQU 85h ; 任务门类型值 DA_386TSS EQU 89h ; 可用 386 任务状态段类型值 DA_386CGate EQU 8Ch ; 386 调用门类型值 DA_386IGate EQU 8Eh ; 386 中断门类型值 DA_386TGate EQU 8Fh ; 386 陷阱门类型值 ; RPL(Requested Privilege Level): 请求特权级,用于特权检查。 ; ; TI(Table Indicator): 引用描述符表指示位 ; TI=0 指示从全局描述符表GDT中读取描述符; ; TI=1 指示从局部描述符表LDT中读取描述符。 ; ;---------------------------------------------------------------------------- ; 选择符类型值说明 ; 其中: ; SA_ : Selector Attribute SA_RPL0 EQU 0 ; ? SA_RPL1 EQU 1 ; ? RPL SA_RPL2 EQU 2 ; ? SA_RPL3 EQU 3 ; ? SA_TIG EQU 0 ; ?TI SA_TIL EQU 4 ; ? ;---------------------------------------------------------------------------- ; 宏 ------------------------------------------------------------------------------------------------- ; ; 描述符 ; usage: Descriptor Base, Limit, Attr ; Base: dd ; Limit: dd (low 20 bits available) ; Attr: dw (lower 4 bits of higher byte are always 0) %macro Descriptor 3 dw %2 & 0FFFFh ; 段界限1 dw %1 & 0FFFFh ; 段基址1 db (%1 >> 16) & 0FFh ; 段基址2 dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 属性1 + 段界限2 + 属性2 db (%1 >> 24) & 0FFh ; 段基址3 %endmacro ; 共 8 字节 ; ; 门 ; usage: Gate Selector, Offset, DCount, Attr ; Selector: dw ; Offset: dd ; DCount: db ; Attr: db %macro Gate 4 dw (%2 & 0FFFFh) ; 偏移1 dw %1 ; 选择子 dw (%3 & 1Fh) | ((%4 << 8) & 0FF00h) ; 属性 dw ((%2 >> 16) & 0FFFFh) ; 偏移2 %endmacro ; 共 8 字节 ; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 3(汇编源代码 , 测试程序(pmtest1.asm) %include "pm.inc" ; 常量, 宏, 以及一些说明 org 07c00h jmp LABEL_BEGIN [SECTION .gdt] ; GDT(定义全局描述符表。注意,为防止误操作,首个GDT表项必须为空) ; 段基址, 段界限, 属性 Descriptor 0, 0, 0 ; 空描述符 LABEL_GDT: LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32; 非一致代码段 LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ; 显存首地址 ; GDT 结束 ; 定义48位的GDT参数结构GdtPtr(16位界限 + 32位基地址) GdtLen equ $ - LABEL_GDT ; GDT长度 GdtPtr dw GdtLen - 1 ; GDT界限 dd 0 ; GDT基地址 ; GDT 选择符(定义代码段和显存段在GDT中的偏移量) SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT ; = 64 SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT ; = 128 ; END of [SECTION .gdt] [SECTION .s16] [BITS 16] LABEL_BEGIN: mov ax, cs ; 设置DS/ES/SS = CS mov ds, ax mov es, ax mov ss, ax mov sp, 100h ; 设置 SP = 100h ; 初始化 32 位代码段描述符中的基址部分 xor eax, eax ; EAX = 0 mov ax, cs shl eax, 4 ; EAX = 代码段基址 add eax, LABEL_SEG_CODE32 ; EAX + 偏移地址 = 32位代码段起始地址 mov word [LABEL_DESC_CODE32 + 2], ax ; B0~15 = AX shr eax, 16 ; EAX >> 16(AX = EAX的高16位) mov byte [LABEL_DESC_CODE32 + 4], al ; B16~23 = AL mov byte [LABEL_DESC_CODE32 + 7], ah ; B24~31 = AH ; 为加载 GDTR 作准备 xor eax, eax mov ax, ds shl eax, 4 add eax, LABEL_GDT ; eax <-- GDT基地址 mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <-- GDT基地址 ; 加载 GDTR lgdt [GdtPtr] ; 关中断 cli ; 打开地址线A20 in al, 92h or al, 00000010b ; or al, 2(关闭:and al,0FDh) out 92h, al ; 准备切换到保护模式(置CR0的PE位为1) mov eax, cr0 or eax, 1 ; PE = 1 mov cr0, eax ; 真正进入保护模式 jmp dword SelectorCode32:0 ; 执行这一句会把SelectorCode32 装入cs, ; 并跳转到Code32Selector:0处 ; END of [SECTION .s16] [SECTION .s32]; 32 位代码段. 由实模式跳入. [BITS 32] LABEL_SEG_CODE32: mov ax, SelectorVideo mov gs, ax ; 视频段选择符(目的) mov edi, (80 * 11 + 79) * 2 ; 屏幕第 11 行, 第 79 列。 mov ah, 0Ch ; 0000: 黑底 1100: 红字 mov al, 'P' mov [gs:edi], ax ; 到此停止 jmp $ ; 死循环 SegCode32Len equ $ - LABEL_SEG_CODE32 ; END of [SECTION .s32] 4(代码解释 , 机器指令LGDT将48位GDT参数(参见下图)加载到寄存器GDTR中,我们代 码中的GDT参数,用结构变量GdtPtr表示。 47 16 15 0 32位GDT基址 16位GDT界限 下面是相关代码: ; 加载 GDTR lgdt [GdtPtr] , 打开A20地址线 由于历史原因,虽然8086处理器只有20位地址线(即只能寻址1MB地址空间),但是如果试图访问超过1MB的地址时,系统并不会发生异常,而只是回卷(wrap)寻址(忽略序号为20的第21位及以上的地址数据)。在推出80286后,不再回卷,可访问超过1MB以上的地址空间。但为了保证对老软件的兼容,IBM想出了一个办法——使用Intel 8042键盘控制器(后来改用92h号端口的第二个二进制位)来控制20号(及以上)地址(Address)位,称为A20线门(A20 line gate)或门A20(Gate-A20))。当A20打开时,不回卷,可访问超过1MB以上的地址空间;当A20关闭时,则回卷,只能寻址1MB地址空间。 在系统启动时,主板上的系统BIOS会打开A20进行内存检测,但在将控制转交给操作系统(磁盘引导扇区)之前,会关闭A20。 为了使用大于1MB的内存空间,在进入保护模式之前,我们先必须打开A20。下面是相关代码: ; 打开地址线A20 in al, 92h or al, 00000010b out 92h, al , 置CR0寄存器的PE位 CR0是80386引入的4个32位的控制寄存器(Control Register)CR0~CR3之一,包含若干控制处理器操作模式和状态的系统控制标志。其中的最低位PE(Protection Enable,保护允许/激活保护)用于允许/禁止保护状态。 相关代码: ; 准备切换到保护模式(置CR0的PE位为1) mov eax, cr0 or eax, 1 mov cr0, eax 控制寄存器 , 跳转进入保护模式 虽然置CR0的PE=1后,系统已经允许在保护模式,但是此时的CS仍然是16位实模式下的值,必须通过一个特殊的跳转指令,从我们程序前面的16位代码([SECTION .s16])跳转到后面的32位代码([SECTION .s32])。至此,才算真正完成从实模式到保护模式的转换。 相关代码: ; 真正进入保护模式 jmp dword SelectorCode32:0 ; 执行这一句会把 SelectorCode32 装入 cs, ; 并跳转到 Code32Selector:0 处 注意,其中jmp指令的红色的数据类型说明符dword(32位的双字)是NASM特有的。 4(编译运行 编译:nasm pmtest1.asm -o pmtest1.bin 写入:用FloppyWriter或WinHex将pmtest1.bin写入软盘映像的引导扇区,并在该扇区的结束处(1FEh)添加启动扇区的结束标55h和0aah 测试:用Bochs虚拟机测试 结果:运行结果如下图所示: 3.2 启动分页机制 进入保护模式,只是启动了分段机制,可以实现保护功能。但是如果要实现虚拟内存管理,还需要启动可选的分页机制。 1(启动分页机制的主要步骤 , 准备PD和PT , 让CR3指向PD , 置CR0寄存器的PG位 跳转启动分页机制 , 短 2(汇编源代码 需要在原pm.inc中增加如下常量定义: ; 为段描述符中属性字对应控制位G(15b)的十六进制值,用于构造描述符 DA_LIMIT_4K EQU 8000h ; 段界限粒度为 4K 字节 ;---------------------------------------------------------------------------- ; 分页机制使用的常量说明 ; 为页目录项PDE和页表项PTE中字对应属性位的值,用于表项的初始化 ;---------------------------------------------------------------------------- PG_P EQU 1 ; 页存在属性位 PG_RWR EQU 0 ; R/W 属性位值, 读/执行 PG_RWW EQU 2 ; R/W 属性位值, 读/写/执行 PG_USS EQU 0 ; U/S 属性位值, 系统级 PG_USU EQU 4 ; U/S 属性位值, 用户级 ;---------------------------------------------------------------------------- 下面是进入保护模式并启动分页机制的测试程序的源代码(pmtest2.asm): 其中 , 红色部分为启动分页机制的代码 , 绿色部分为返回实模式的代码 , 其余部分为进入保护模式的代码 %include "pm.inc" ; 常量, 宏, 以及一些说明 PageDirBase equ 200000h ; 页目录开始地址: 2M PageTblBase equ 201000h ; 页表开始地址: 2M+4K org 0100h jmp LABEL_BEGIN [SECTION .gdt] ; GDT ; 段基址, 段界限, 属性 LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符 LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, DA_DRW; Normal 描述符 LABEL_DESC_PAGE_DIR: Descriptor PageDirBase, 4095, DA_DRW ; Page Directory LABEL_DESC_PAGE_TBL: Descriptor PageTblBase, 1023, DA_DRW|DA_LIMIT_4K ; Page Tables LABEL_DESC_CODE32: Descriptor 0, SegCode32Len-1, DA_C+DA_32 ; 非一致代码段, 32 LABEL_DESC_CODE16: Descriptor 0, 0ffffh, DA_C ; 非一致代码段, 16 LABEL_DESC_DATA: Descriptor 0, DataLen-1, DA_DRW ; Data LABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA + DA_32 ; Stack, 32 位 LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ; 显存首地址 ; GDT 结束 GdtLen equ $ - LABEL_GDT ; GDT长度 GdtPtr dw GdtLen - 1 ; GDT界限 dd 0 ; GDT基地址 ; GDT 选择符 SelectorNormal equ LABEL_DESC_NORMAL - LABEL_GDT SelectorPageDir equ LABEL_DESC_PAGE_DIR - LABEL_GDT SelectorPageTbl equ LABEL_DESC_PAGE_TBL - LABEL_GDT SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT SelectorCode16 equ LABEL_DESC_CODE16 - LABEL_GDT SelectorData equ LABEL_DESC_DATA - LABEL_GDT SelectorStack equ LABEL_DESC_STACK - LABEL_GDT SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT ; END of [SECTION .gdt] [SECTION .data1] ; 数据段 ALIGN 32 [BITS 32] LABEL_DATA: SPValueInRealMode dw 0 ; 字符串 PMMessage: db "In Protect Mode now. ^-^", 0; 进入保护模式后显示此字符串 OffsetPMMessage equ PMMessage - $$ DataLen equ $ - LABEL_DATA ; END of [SECTION .data1] ; 全局堆栈段 [SECTION .gs] ALIGN 32 [BITS 32] LABEL_STACK: times 512 db 0 TopOfStack equ $ - LABEL_STACK - 1 ; END of [SECTION .gs] [SECTION .s16] [BITS 16] LABEL_BEGIN: mov ax, cs mov ds, ax mov es, ax mov ss, ax mov sp, 0100h mov [LABEL_GO_BACK_TO_REAL+3], ax mov [SPValueInRealMode], sp ; 初始化 16 位代码段描述符 mov ax, cs movzx eax, ax shl eax, 4 add eax, LABEL_SEG_CODE16 mov word [LABEL_DESC_CODE16 + 2], ax shr eax, 16 mov byte [LABEL_DESC_CODE16 + 4], al mov byte [LABEL_DESC_CODE16 + 7], ah ; 初始化 32 位代码段描述符 xor eax, eax mov ax, cs shl eax, 4 add eax, LABEL_SEG_CODE32 mov word [LABEL_DESC_CODE32 + 2], ax shr eax, 16 mov byte [LABEL_DESC_CODE32 + 4], al mov byte [LABEL_DESC_CODE32 + 7], ah ; 初始化数据段描述符 xor eax, eax mov ax, ds shl eax, 4 add eax, LABEL_DATA mov word [LABEL_DESC_DATA + 2], ax shr eax, 16 mov byte [LABEL_DESC_DATA + 4], al mov byte [LABEL_DESC_DATA + 7], ah ; 初始化堆栈段描述符 xor eax, eax mov ax, ds shl eax, 4 add eax, LABEL_STACK mov word [LABEL_DESC_STACK + 2], ax shr eax, 16 mov byte [LABEL_DESC_STACK + 4], al mov byte [LABEL_DESC_STACK + 7], ah ; 为加载 GDTR 作准备 xor eax, eax mov ax, ds shl eax, 4 add eax, LABEL_GDT ; eax <- gdt 基地址 mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址 ; 加载 GDTR lgdt [GdtPtr] ; 关中断 cli ; 打开地址线A20 in al, 92h or al, 00000010b out 92h, al ; 准备切换到保护模式 mov eax, cr0 or eax, 1 mov cr0, eax ; 真正进入保护模式 jmp dword SelectorCode32:0 ; 执行这一句会把SelectorCode32装入cs, ; 并跳转到Code32Selector:0处 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 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] [SECTION .s32]; 32 位代码段. 由实模式跳入. [BITS 32] LABEL_SEG_CODE32: call SetupPaging mov ax, SelectorData mov ds, ax ; 数据段选择符 mov ax, SelectorVideo mov gs, ax ; 视频段选择符 mov ax, SelectorStack mov ss, ax ; 堆栈段选择符 mov esp, TopOfStack ; 下面显示一个字符串 mov ah, 0Ch ; 0000: 黑底 1100: 红字 xor esi, esi xor edi, edi mov esi, OffsetPMMessage ; 源数据偏移 mov edi, (80 * 10 + 0) * 2 ; 目的数据偏移。屏幕第 10 行, 第 0 列。 cld .1: lodsb test al, al jz .2 mov [gs:edi], ax add edi, 2 jmp .1 .2: ; 显示完毕 ; 到此停止 jmp SelectorCode16:0 ; 启动分页机制 -------------------------------------------------------------- SetupPaging: ; 为简化处理, 所有线性地址对应相等的物理地址. ; 首先初始化页目录 mov ax, SelectorPageDir; 此段首地址为 PageDirBase mov es, ax mov ecx, 1024 ; 共 1K 个表项 xor edi, edi xor eax, eax mov eax, PageTblBase | PG_P | PG_USU | PG_RWW .1: stosd ; 存储双字串:EAX -> ES:EDI, EDI += 4, ECX-- add eax, 4096 ; 为了简化, 所有页表在内存中是连续的. loop .1 ; 再初始化所有页表 (1K个, 4M内存空间) mov ax, SelectorPageTbl ; 此段首地址为 PageTblBase mov es, ax mov ecx, 1024 * 1024 ; 共 1M 个页表项, 也即有 1M 个页 xor edi, edi xor eax, eax mov eax, PG_P | PG_USU | PG_RWW .2: stosd add eax, 4096 ; 每一页指向 4K 的空间 loop .2 mov eax, PageDirBase ; CR3 => 页目录起始地址 mov cr3, eax mov eax, cr0 or eax, 80000000h mov cr0, eax ; CR0的PG位=1 jmp short .3 .3: nop ret ; 分页机制启动完毕 ---------------------------------------------------------- SegCode32Len equ $ - LABEL_SEG_CODE32 ; END of [SECTION .s32] ; 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 eax, 7FFFFFFEh ; PE=0, PG=0 mov cr0, eax LABEL_GO_BACK_TO_REAL: jmp 0:LABEL_REAL_ENTRY ; 段地址会在程序开始处被设置成正确的值 Code16Len equ $ - LABEL_SEG_CODE16 ; END of [SECTION .s16code] 汇编成pmtest2.com后在Windows的命令行窗口执行,不会有任何输出。用VMware下的Ubuntu或WinHex放进MyOS.img中,在Boches虚拟机上的DOS环境下执行,结果显示为: 3(代码解释 我们采用基本的4KB分页大小,并将页目录PD的开始地址PageDirBase定在200000h(2M)处,大小为4KB(1K个目录项)。将页表PT的开始地址PageTblBase定在201000h(2M+4K)处,紧跟在PD之后,大小为1KB*4K(1M个表项)。参见下表: PageDirBase (200000h) PageTblBase (201000h) L PDE 0 PDE 1024 第0帧 个 4096B PDE „„ PDE 每 个第1帧 PTE 页4096B PTE 表 对 应 „„ 1 共 0 1 共 „„ PTE 2 M1 4个PTE 0 个页 2 PTE 页 , 每个页表 4 4 个含1024个 第1023帧 G „„ 页PTE B 4096B 表 线, PTE 性占 地 用 址 4 空M „„ 间 B„„ 内 存 空PTE 间 PTE 第1048575帧 „„ 4096B 4GB PTE H 在全局描述符表GDT中增加了PD和PT的段描述符。下面这两个表的表项格式: 31 12 11 10 9 8 7 6 5 4 3 2 1 0 I P P U R P 页表物理地址的高20位 Ignored g A C W / / P S n D T S W 4KB分页时的PDE(页目录项)格式 31 12 11 10 9 8 7 6 5 4 3 2 1 0 P P P U R 页帧物理地址的高20位(帧号) Ignored G A D A C W / / P T D T S W 4KB分页时的PTE(页表项)格式 我们用处理器的存储(双字)串指令STOSD来初始化这两个表: stosd ; 存储双字串:EAX -> ES:EDI, EDI += 4, ECX-- 该指令将EAX中的值存储到ES:EDI指定的内存地址中,然后让EDI加4、ECX减1,用LOOP语句进行循环,直到ECX减为0。 对页目录,其中的ES = 段选择符SelectorPageDir,其基址为PageDirBase(200000h = 2M),EDI的初值为0,ECX的初值为1024(个页目录表项)。 页目录的表项PDE的初值:EAX = PageTblBase | PG_P | PG_USU | PG_RWW,每循环一次EAX加4096(4K),相当于对应页表的物理地址的高20位加1(即每个页表的大小只有4KB,占1个页)。我们的程序只有唯一的一个页表段,含1024个页表。 对页表,ES = 段选择符SelectorPageTbl,其基址为PageTblBase(201000h = 2M + 4K),EDI的初值也为0,但ECX的初值为1024*1024(共1M个页表项,占4MB空间,对应于4GB的线性地址空间)。 页表项PTE的初值:EAX = PG_P | PG_USU | PG_RWW(即从第0帧开始),每循环一次EAX加4096(4K),相当于对应帧的物理地址高20位加1(即帧号加1)。 在准备好PD和PT后,我们让三号控制寄存器CR3指向页目录的起始地址,并设零号控制寄存器CR0的最高位PG(Paging,分页)为1,即可启动分页机制: mov eax, PageDirBase mov cr3, eax mov eax, cr0 or eax, 80000000h mov cr0, eax 其中,PDBR = Page-Directory Base Register 页目录基址寄存器。 4 获取内存大小 4.1 int 12h PC机在8086实模式下,可以调用BIOS 12h中断来确定内存容量(最大640KB),返回值放在AX中,以1KB(1024B)为单位列出可用的RAM空间。例如,用Debug执行int 12h,结果AX=280h=640,即实模式下可用的内容容量为640KB。参见下图: 4.2 int 15h, AX = E801h 对现代的PC机(80386+),还可以通过调用BIOS中断15h的E801h号功能来确定内 存的容量。 返回参数: , CF = 0:成功、CF = 1:出错(CF为标志寄存器中的进位标志) , AX = 1MB~16MB之间的扩展内存,以KB为单位,最大值为3C00h(对应于15MB) , BX = 16MB以上的扩展内存,以64KB为单位 , CX = 1MB~16MB之间的配置内存,以KB为单位 , DX = 16MB以上的配置内存,以64KB为单位 例如(ms1.asm) org 100h ; 可汇编成COM文件 mov ax,0E801h ; 功能号 int 15h ; 中断调用 jc LB_fail ; 出错跳转 push dx ; 保存DX push cx ; 保存CX push bx ; 保存BX call DispVal ; 显示AX pop ax ; 恢复BX call DispVal ; 显示BX pop ax ; 恢复CX call DispVal ; 显示CX pop ax ; 恢复DX call DispVal ; 显示DX ret ; 返回 LB_fail: ; 调用失败时显示“Failed!”字符串 mov bp,FailMsg ; BP=当前串的偏移地址 mov ax,ds ; ES:BP = 串地址 mov es,ax ; 置ES=DS mov cx,7 ; CX = 串长(=9) mov ax,1301h ; AH = 13h(功能号)、AL = 01h(光标置于串尾) mov bx,000Fh ; 页号为0(BH = 0) 黑底白字(BL = 0Fh) mov dx,0 ; 列号=0(DL=0) 行号=0(DH=0) int 10h ; 显示中断 ret ; 退出程序 ; 定义字符串常量 FailMsg: db "Failed!" ; 中断调用失败时显示的字符串 ; 显示数据值十六进制串函数 DispVal: ; 显示16位整数值串(以AX为传递参数) mov dx,ax ; 保存传递参数AX的值 ; 显示高4位 and ax,0F000h ; 取出最高的4位 shr ax,12 ; 右移12位 call ShowChar ; 调用显示字符函数 ; 显示中高4位 mov ax,dx ; 恢复传递参数AX的值 and ax,0F00h ; 取出中高的4位 shr ax,8 ; 右移8位 call ShowChar ; 调用显示字符函数 ; 显示中低4位 mov ax,dx ; 恢复传递参数AX的值 and ax,0F0h ; 取出中低的4位 shr ax,4 ; 右移4位 call ShowChar ; 调用显示字符函数 ; 显示低4位 mov ax,dx ; 恢复传递参数AX的值 and ax,0Fh ; 取出最低的4位 call ShowChar ; 调用显示字符函数 ; 显示空格符 mov al,20h ; AL = 空格符 mov ah,0Eh ; 功能号(以电传方式显示单个字符) mov bl,0 ; 对文本方式置0 int 10h ; 调用10H号中断 ret ; 显示单个十六进制字符函数 ShowChar: ; 显示一个十六进制数字符:0~9、A~F(以AL为传递参数) cmp al,10 ; AL < 10 ? jl digital ; AL < 10:跳转到digital add al,7 ; AL >= 10:显示字母( = 数值+=37h) digital: ; 数字 add al,30h ; 数字字符 = 数值+=30h mov ah,0Eh ; 功能号(以电传方式显示单个字符) mov bl,0 ; 对文本方式置0 int 10h ; 调用10H号中断 ret 汇编成ms1.com后在Windows的命令行窗口执行,会输出失败字符串。用WinImage将其放进MyOS.img中,在Boches虚拟机上的DOS环境下执行,我的机器上显示为: 即: , 1MB~16MB之间的扩展内存 = 3C00h*1KB = 15360KB = 15MB , 16MB以上的扩展内存 = 100h*64KB = 256*64KB = 16384KB = 16MB。 , 总内存容量为1MB+15MB+16MB = 32MB??? 原因在于我们使用了Bochs虚拟机的默认内存大小设置(=32MB),可以修改Bochs虚拟机的配置文件,将原来的megs: 32改大一些(如1GB)megs: 1024。参加下图: 重新运行虚拟机,结果如下图所示: , 1MB~16MB之间的扩展内存 = 3C00h*1KB = 15360KB = 15MB , 16MB以上的扩展内存 = 3F00h*64KB = 16128*64KB = 1032192KB = 1008MB。 , 总内存容量为1MB+15MB+1008MB = 1024MB = 1GB 如果将ms1.com写入U盘的引导扇区,并用它启动计算机,在我的电脑上显示为: 3C00 DE30 3C00 DE30 因DE30h * 64 KB = 56880 * 64 KB = 3555 MB,所以再 + 16MB = 3571 MB = 3.48GB,与WinXP显示的一致。 4.3 int 15h, EAX = E820h 对现代的PC机还可以通过(多次)调用BIOS中断15h的E820h号功能来确定内存的容量和分布。 入口参数: , EAX = 功能号E820h , EBX = 后续值,初值必须=0 , ES:DI = 缓冲区地址 , ECX = 缓冲区字节数(必须>=20,一般取为20) , EDX = 534D4150h("SMAP")为校验标志 出口参数: , CF = 0:成功、CF = 1:出错(CF为标志寄存器中的进位标志) , EAX = 534D4150h("SMAP") , ES:DI = 入口时的值 , ECX = 填充的字节数(>=20) , EBX =后续值,若=0则内存段的地址范围描述符已读完 地址范围描述符结构(Address Range Discriptor Structure,ARDS): ARDS 偏移量 数据类型 字节数 描述 0 QWORD 8 基址 8 QWORD 8 长度 16 DWORD 4 类型 其中的地址范围类型的取值: :OS可用的RAM内存 , 1 , 2:保留,OS不可用(如系统ROM、存储映像设备) , 3:ACPI(Advanced Configuration and Power Management Interface,高级配置和电 源管理接口)回收内存(读ACPI表后OS可用) , 4:ACPI NVS内存(OS必需在NVS会话之间保存该内存) , 其余值:未定义,视为保留 例如(ms2.asm): org 100h ; 可汇编成COM文件 ;org 7C00h ; 用于引导扇区 mov ax,cs ; DS = CS, SS = CS mov ds,ax mov ss,ax mov sp,100h-4 ; 可汇编成COM文件 ;mov sp,7C00h-4 ; 用于引导扇区 mov es,ax ; ES = CS mov ebx,0 ; EBX = 0 (初始值) mov di,Buf ; ES:DI = 返回值的缓冲区起始地址 LB_loop: ; 调用15h中断的E820h功能获取内存容量 mov eax,0E820h ; 功能号 mov ecx,20 ; 缓冲区大小 mov edx,534D4150h ; "SMAP" 校验标志 int 15h ; 中断调用 jc LB_fail ; 出错跳转 add di,20 ; 缓冲区指针后移20个字节 inc word [Numb] ; 内存分段数加一 cmp ebx,0 ; EBX = 0? jne LB_loop ; EBX != 0:继续调用 mov di, Buf ; EBX = 0:结束,DI = 缓冲区起始地址(用于显示) jmp LB_OK ; 跳转到LB_OK,开始显示返回值 LB_fail: ; 调用失败时显示“Failed!”字符串 mov bp,FailMsg ; BP=当前串的偏移地址 mov ax,ds ; ES:BP = 串地址 mov es,ax ; 置ES=DS mov cx,7 ; CX = 串长(=9) mov ax,1301h ; AH = 13h(功能号)、AL = 01h(光标置于串尾) mov bx,000Fh ; 页号为0(BH = 0) 黑底白字(BL = 0Fh) mov dx,0 ; 列号=0(DL=0) 行号=0(DH=0) int 10h ; 显示中断 mov word [Numb],0 ; 置内存分段数=0(不显示返回值) ret ; 退出程序 LB_OK: ; 循环显示返回段值 cmp word [Numb],0 ; Numb = 0? je LB_out ; Numb = 0:退出程序 mov cx,20 ; CX = 20(行内循环初值) LB_lloop: ; 行内循环 mov al, byte [di] ; AL = 当前字节值 call DispByte ; 调用显示字节十六进制值的函数 inc di ; DI++ dec cx ; CX-- jnz LB_lloop ; 行内循环 dec word [Numb] ; Numb-- call DispCrnl ; 回车换行 jnz LB_OK ; 显示下一个内存段值 LB_out: ; 退出程序 ret ; 返回 ; 显示回车换行符 DispCrnl: mov al,0Ah ; AL = 换行符 mov ah,0Eh ; 功能号(以电传方式显示单个字符) mov bl,0 ; 对文本方式置0 int 10h ; 调用10H号中断 mov al,0Dh ; AL = 回车符 mov ah,0Eh ; 功能号(以电传方式显示单个字符) mov bl,0 ; 对文本方式置0 int 10h ; 调用10H号中断 ret ; 显示字节数据值十六进制串函数 DispByte: ; 显示字节数值串(以AL为传递参数) mov dl,al ; 保存传递参数AX的值 ; 显示高4位 and al,0F0h ; 取出高4位 shr al,4 ; 右移4位 call ShowChar ; 调用显示字符函数 ; 显示低4位 mov al,dl ; 恢复传递参数AX的值 and al,0Fh ; 取出低4位 call ShowChar ; 调用显示字符函数 ; 显示空格符 mov al,20h ; AL = 空格符 mov ah,0Eh ; 功能号(以电传方式显示单个字符) mov bl,0 ; 对文本方式置0 int 10h ; 调用10H号中断 ; 如果当前字节的序号%8=0,则多显示一个空格(分隔结构中的字段) mov ax,21 ; AX = 21 - CX sub ax,cx and ax,7 ; AX & 0111 b cmp ax,0 ; AX % 8 = 0 ? jnz LB_ret ; != 0:返回、= 0:再显示一个空格符 mov al,20h ; AL = 空格符 mov ah,0Eh ; 功能号(以电传方式显示单个字符) mov bl,0 ; 对文本方式置0 int 10h ; 调用10H号中断 LB_ret: ret ; 显示单个十六进制字符函数 ShowChar: ; 显示一个十六进制数字符:0~9、A~F(以AL为传递参数) cmp al,10 ; AL < 10 ? jl digital ; AL < 10:跳转到digital add al,7 ; AL >= 10:显示字母( = 数值+=37h) digital: ; 数字 add al,30h ; 数字字符 = 数值+=30h mov ah,0Eh ; 功能号(以电传方式显示单个字符) mov bl,0 ; 对文本方式置0 int 10h ; 调用10H号中断 ret ; 定义变量和缓冲区 Numb dw 0 ; 内存分段数,初值=0 FailMsg: db "Failed!" ; 中断调用失败时显示的字符串 Buf: times 160 db 0 ; 存放中断返回值的缓冲区 汇编成ms2.com后在Windows的命令行窗口执行,会输出失败字符串。用WinImage 将其放进MyOS.img中,在(内存设为1GB的)Boches虚拟机上的DOS环境下执行,我 的机器上显示为: ARDS 序号 基址 长度(字节数) 类型 1 00000000 0009F000(636KB) 1 2 0009F000 00001000(4KB) 2 3 000E8000 00018000(96KB) 2 4 00100000 3FEF0000(1023MB) 1 5 3FFF0000 00010000(64KB) 3 6 FFFC0000 00040000(256KB) 2 如果将ms2.asm开始处的org和SP赋值改为7C00h和7C00h-4,重新编译后写入U盘 的引导扇区,并用它启动计算机,在我的台式电脑上显示的结果为: ARDS 序号 基址 长度(字节数) 类型 1 00000000 0009E800(634KB) 1 2 0009E800 00001800(6KB) 2 3 000E0000 00020000(128KB) 2 4 00100000 DF202000(3570MB) 1 5 DF302000 00058000(352KB) 4 6 DF35A000 00246000(2328KB) 2 7 DF5A0000 00011000(68KB) 4 8 DF5B1000 00014000(80KB) 2 9 DF5C5000 00002000(8KB) 1 10 DF5C7000 00001000(4KB) 3 11 DF5C8000 00008000(32KB) 2 12 DF5D0000 0000A000(40KB) 4 13 DF5DA000 0005C000(368KB) 2 14 DF636000 00043000(268KB) 4 15 DF679000 00187000(1564KB) 1 16 100000000 11F800000(4600MB) 1 17 FED1C000 00004000(16KB) 2 18 FF0000000 01000000(16MB) 2 可用的内存空间为:634 KB + 3570 MB + 8 KB + 1564 KB + 4600 MB = 8172 MB。 在我的笔记本电脑上显示的结果为: ARDS 序号 基址 长度(字节数) 类型 1 00000000 0009D400(629KB) 1 2 0009D400 00002C00(11KB) 2 3 000E0000 00020000(128KB) 2 4 00100000 774A7000(1909MB) 1 5 775A7000 00048000(288KB) 4 6 775EF000 0000B000(44KB) 3 7 775FA000 00003000(12KB) 4 8 775FD000 00025000(148KB) 2 9 77622000 00008000(32KB) 4 10 7762A000 00003000(12KB) 2 11 7762D000 00005000(20KB) 4 12 77632000 00020000(40KB) 2 13 77652000 00043000(268KB) 4 14 77695000 0016B000(1452KB) 1 15 79E00000 02200000(34MB) 2 16 E0000000 10000000(256MB) 2 17 FED1C000 00004000(16KB) 2 18 FF0000000 01000000(16MB) 2 可用的内存空间为:629 KB + 1909 MB + 1452 KB = 1911 MB。 实验7 , 了解x86的内存管理方式。 , 了解IA-32的寻址方式。 , 掌握进入保护模式的步骤。 , 实现进入保护模式的测试程序pmtest1.asm。 实验8 , 掌握启动分页机制的步骤。 , 实现启动分页机制的测试程序pmtest2.asm。 , 实现获取内存容量的两个程序ms1.asm和ms2.asm。 , 修改ms2.asm: , 使其只保存和显示类型为1的行。 , 并对每行中字段数据的显示,从低位字节在前,改为高位字节在前。 , (选做)最后计算并显示可用内存的总容量。 例如: 在内存大小设置为1024MB的Bochs虚拟机上: 在内存大小为8GB的台式机上:
/
本文档为【内存管理与保护模式】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索