参考
https://blog.51cto.com/13475106/category6.html及狄泰软件相关课程
一.x86系列处理器上的页式内存管理
1.硬件层直接支持内存分页机制
2.默认情况下不使用分页机制(段式内存管理)
3.分页机制(进行配置)启动后,使用二级页表对内存进行管理
x86系列处理器的分页方式(32位)
如图所示,32位被分成了三部分
1.在低12位中,表示的是页内偏移地址
2.在中间10位,用于在子页表中查找目标页地址
3.在最高的10位中,用于子页目录中查找页表地址
A.下面由一张图来展示分页机制
B.x86系列处理器的分页方式(32位)
1.在页目录大小中,2的十次方项,每项4字节,一共为4K
2.在子页表大小中,2的十次方项,每项4字节,一共为4K
3.在页大小中,2的十二次方,一共为4K
由上可以得出
1.页目录占用1内存页-可访问1024个子页表
2.单个子页表占用1内存页-可访问1024个页面
3.页面起始地址按4K字节对齐-总是4096整数倍
4.分页后可访问的虚拟内存空间为:4K(1024*1024)=4G
X86简单的分页构建方式
可以通过for循环构建目录,子页表,主要原因是一个一个的生成的
C.x86系列处理器上的页属性
1.由于物理页面的地址必须按照4K字节对齐
2.由此可得,页目录可使用地址的低12位进行属性描述
在x86系列处理器上查看页属性的说明
D.x86对分页的硬件支持--代码上
如下所示
1.将页目录起始地址放置到cr3-该寄存器类似指针指向页目录起始地址
2.将cr0里面的值取出放置到eax寄存器中,
3.将cr0里面的值所对应二进制最高位置1--硬件级开启分页机制
在这里需要注意的是
1.loop指令-该指令表示的是循环指令
mov ax,0 mov cx,10 Label: add ax,cx loop Label
在这里表示是将cx减1,对cx进行判断,若cx不为0,则执行标签处Label的代码
2.-stosb/stosw/stosd
表示的是把al/ax/eax中的值存储到edi指向的内存单元中,同时edi的值根据方向标志增加或减少(cld/std)
mov es,ax mov edi, mov eax,0XFF cld stosd
编程实现
loader.asm与inc.asm的设置
%include "inc.asm" PageDirBase equ 0x200000 PageTblBase equ 0x201000 org 0x9000 jmp ENTRY_SEGMENT [section .gdt] ; GDT definition ; 段基址, 段界限, 段属性 GDT_ENTRY : Descriptor 0, 0, 0 CODE32_DESC : Descriptor 0, Code32SegLen - 1, DA_C + DA_32 VIDEO_DESC : Descriptor 0xB8000, 0x07FFF, DA_DRWA + DA_32 DATA32_DESC : Descriptor 0, Data32SegLen - 1, DA_DRW + DA_32 STACK32_DESC : Descriptor 0, TopOfStack32, DA_DRW + DA_32 PAGE_DIR_DESC : Descriptor PageDirBase, 4095, DA_DRW + DA_32 PAGE_TBL_DESC : Descriptor PageTblBase, 1023, DA_DRW + DA_LIMIT_4K + DA_32 ; GDT end GdtLen equ $ - GDT_ENTRY GdtPtr: dw GdtLen - 1 dd 0 ; GDT Selector Code32Selector equ (0x0001 << 3) + SA_TIG + SA_RPL0 VideoSelector equ (0x0002 << 3) + SA_TIG + SA_RPL0 Data32Selector equ (0x0003 << 3) + SA_TIG + SA_RPL0 Stack32Selector equ (0x0004 << 3) + SA_TIG + SA_RPL0 PageDirSelector equ (0x0005 << 3) + SA_TIG + SA_RPL0 PageTblSelector equ (0x0006 << 3) + SA_TIG + SA_RPL0 ; end of [section .gdt] TopOfStack16 equ 0x7c00 [section .dat] [bits 32] DATA32_SEGMENT: DTOS db "D.T.OS!", 0 DTOS_OFFSET equ DTOS - $$ HELLO_WORLD db "Hello World!", 0 HELLO_WORLD_OFFSET equ HELLO_WORLD - $$ Data32SegLen equ $ - DATA32_SEGMENT [section .s16] [bits 16] ENTRY_SEGMENT: mov ax, cs mov ds, ax mov es, ax mov ss, ax mov sp, TopOfStack16 ; initialize GDT for 32 bits code segment mov esi, CODE32_SEGMENT mov edi, CODE32_DESC call InitDescItem mov esi, DATA32_SEGMENT mov edi, DATA32_DESC call InitDescItem mov esi, STACK32_SEGMENT mov edi, STACK32_DESC call InitDescItem ; initialize GDT pointer struct mov eax, 0 mov ax, ds shl eax, 4 add eax, GDT_ENTRY mov dword [GdtPtr + 2], eax ; 1. load GDT lgdt [GdtPtr] ; 2. close interrupt cli ; 3. open A20 in al, 0x92 or al, 00000010b out 0x92, al ; 4. enter protect mode mov eax, cr0 or eax, 0x01 mov cr0, eax ; 5. jump to 32 bits code jmp dword Code32Selector : 0 ; esi --> code segment label ; edi --> descriptor label InitDescItem: push eax mov eax, 0 mov ax, cs shl eax, 4 add eax, esi mov word [edi + 2], ax shr eax, 16 mov byte [edi + 4], al mov byte [edi + 7], ah pop eax ret [section .s32] [bits 32] CODE32_SEGMENT: mov ax, VideoSelector mov gs, ax mov ax, Stack32Selector mov ss, ax mov eax, TopOfStack32 mov esp, eax mov ax, Data32Selector mov ds, ax mov ebp, DTOS_OFFSET mov bx, 0x0C mov dh, 12 mov dl, 33 call PrintString mov ebp, HELLO_WORLD_OFFSET mov bx, 0x0C mov dh, 13 mov dl, 31 call PrintString call SetupPage jmp $ ; ; SetupPage: push eax push ecx push edi push es mov ax, PageDirSelector mov es, ax mov ecx, 1024 ; 1K sub page tables mov edi, 0 mov eax, PageTblBase | PG_P | PG_USU | PG_RWW cld stdir: stosd add eax, 4096 loop stdir mov ax, PageTblSelector mov es, ax mov ecx, 1024 * 1024 ; 1M pages mov edi, 0 mov eax, PG_P | PG_USU | PG_RWW cld sttbl: stosd add eax, 4096 loop sttbl mov eax, PageDirBase mov cr3, eax mov eax, cr0 or eax, 0x80000000 mov cr0, eax pop es pop edi pop ecx pop eax ret ; ds:ebp --> string address ; bx --> attribute ; dx --> dh : row, dl : col PrintString: push ebp push eax push edi push cx push dx print: mov cl, [ds:ebp] cmp cl, 0 je end mov eax, 80 mul dh add al, dl shl eax, 1 mov edi, eax mov ah, bl mov al, cl mov [gs:edi], ax inc ebp inc dl jmp print end: pop dx pop cx pop edi pop eax pop ebp ret Code32SegLen equ $ - CODE32_SEGMENT [section .gs] [bits 32] STACK32_SEGMENT: times 1024 * 4 db 0 Stack32SegLen equ $ - STACK32_SEGMENT TopOfStack32 equ Stack32SegLen - 1
; Segment Attribute DA_32 equ 0x4000 DA_LIMIT_4K EQU 0x8000 DA_DR equ 0x90 DA_DRW equ 0x92 DA_DRWA equ 0x93 DA_C equ 0x98 DA_CR equ 0x9A DA_CCO equ 0x9C DA_CCOR equ 0x9E ; Segment Privilege DA_DPL0 equ 0x00 ; DPL = 0 DA_DPL1 equ 0x20 ; DPL = 1 DA_DPL2 equ 0x40 ; DPL = 2 DA_DPL3 equ 0x60 ; DPL = 3 ; Special Attribute DA_LDT equ 0x82 DA_TaskGate equ 0x85 ; 任务门类型值 DA_386TSS equ 0x89 ; 可用 386 任务状态段类型值 DA_386CGate equ 0x8C ; 386 调用门类型值 DA_386IGate equ 0x8E ; 386 中断门类型值 DA_386TGate equ 0x8F ; 386 陷阱门类型值 ; Selector Attribute SA_RPL0 equ 0 SA_RPL1 equ 1 SA_RPL2 equ 2 SA_RPL3 equ 3 SA_TIG equ 0 SA_TIL equ 4 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 属性位值, 用户级 ; 描述符 ; 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 & 0xFFFF ; 段界限1 dw %1 & 0xFFFF ; 段基址1 db (%1 >> 16) & 0xFF ; 段基址2 dw ((%2 >> 8) & 0xF00) | (%3 & 0xF0FF) ; 属性1 + 段界限2 + 属性2 db (%1 >> 24) & 0xFF ; 段基址3 %endmacro ; 共 8 字节 ; 门 ; usage: Gate Selector, Offset, DCount, Attr ; Selector: dw ; Offset: dd ; DCount: db ; Attr: db %macro Gate 4 dw (%2 & 0xFFFF) ; 偏移地址1 dw %1 ; 选择子 dw (%3 & 0x1F) | ((%4 << 8) & 0xFF00) ; 属性 dw ((%2 >> 16) & 0xFFFF) ; 偏移地址2 %endmacro
设置的过程如下图所示
通过make之后得到的结果
在这里的打印结果为字符串,并没有分页的显示效果,在后面的博客会对该次实验进行解释
小结
1.x86处理器直接支持内存分页机制
2.分页机制启动后,使用二级页表对内存进行管理
3.页目录和单个子页表占用1内存页
4.页面起始地址按4K字节对齐
5.分页后可访问的虚拟内存空间为4G