分页机制总结
来源:互联网 发布:多表连接查询sql语句 编辑:程序博客网 时间:2024/06/03 14:08
【0】写在前面(分页机制)
- 0.0) source code from orange’s implemention of a os and text description from Zhaojiong’s perfect analysis of Linux kernel and for complete code ,please visit https://github.com/pacosonTang/Orange-s-OS/blob/master/p67.asm
- 0.1)本代码旨在演示 怎样开启分页机制 + 怎样构建页目录和页表
- 0.3)本文 只对 与 分页机制的 代码进行简要注释,言简意赅;
- 0.4) 为了让广大小白(像我一样对os分页机制不觉明里的小白)真真切切了解分页机制,即使这篇文章是转载自 “0.0” 中的两本,但我还是将本文归为原创以推荐到博客首页;(版权,我已在0.0中声明了)
; ==========================================; pmtest6.asm; 编译方法:nasm pmtest6.asm -o pmtest6.com; ==========================================%include "pm.inc" ; 常量, 宏, 以及一些说明PageDirBase equ 200000h ; 页目录开始地址: 2MPageTblBase equ 201000h ; 页表开始地址: 2M+4Korg 0100h jmp LABEL_BEGIN[SECTION .gdt]; GDT; 段基址, 段界限, 属性LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符LABEL_DESC_NORMAL: Descriptor 0, 0ffffh, DA_DRW ; Normal 描述符
; 页目录描述符 [add]
LABEL_DESC_PAGE_DIR: Descriptor PageDirBase, 4095, DA_DRW;Page Directory
; 页表描述符 [add]
LABEL_DESC_PAGE_TBL: Descriptor PageTblBase, 1023, DA_DRW|DA_LIMIT_4K;Page TablesLABEL_DESC_CODE32: Descriptor 0, SegCode32Len-1, DA_C+DA_32 ; 非一致代码段, 32LABEL_DESC_CODE16: Descriptor 0, 0ffffh, DA_C ; 非一致代码段, 16LABEL_DESC_DATA: Descriptor 0, DataLen-1, DA_DRW ; DataLABEL_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_GDTSelectorPageDir equ LABEL_DESC_PAGE_DIR - LABEL_GDTSelectorPageTbl equ LABEL_DESC_PAGE_TBL - LABEL_GDTSelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDTSelectorCode16 equ LABEL_DESC_CODE16 - LABEL_GDTSelectorData equ LABEL_DESC_DATA - LABEL_GDTSelectorStack equ LABEL_DESC_STACK - LABEL_GDTSelectorVideo 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 0TopOfStack 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:
; 开启分页机制的初始化工作,该指令执行后,cpu即可开启分页机制[add]
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
; [add]启动分页机制 ————————————————————–
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 ; PageTblBase equ 201000h 页表开始地址: 2M+4K
; 这里是在初始化页目录中项的内容,即对应页表的内存地址
.1: stosd
;stosb, stosw, stosd 把al/ ax/ eax的内容存储到 es:edi 指向的内存单元中, 该指令执行后,edi自增1
add eax, 4096
; 为了简化, 所有页表在内存中是连续的.,每个页表占用4k字节空间
loop .1
; 初始化页目录的项内容 over
; 再初始化所有页表 (1K 个, 4M 内存空间)的项内容,即页表的项存储的是内存地址的高20位地址;
mov ax, SelectorPageTbl ; 此段首地址为 PageTblBase mov es, ax mov ecx, 1024 * 1024 ; 共 1M 个页表项, 也即有 1M 个页 (因为1k个页目录项,每个目录项映射到1k个页表项) xor edi, edi xor eax, eax mov eax, PG_P | PG_USU | PG_RWW.2: stosd add eax, 4096 ; 每一页指向 4K 的空间 loop .2
; 初始化页表的项内容 over
mov eax, PageDirBase
; 加载页目录的基地址到 cr3
mov cr3, eax
; 设置cr0的PG位=1,开启分页机制
mov eax, cr0 or eax, 80000000h mov cr0, eax 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, eaxLABEL_GO_BACK_TO_REAL: jmp 0:LABEL_REAL_ENTRY ; 段地址会在程序开始处被设置成正确的值Code16Len equ $ - LABEL_SEG_CODE16; END of [SECTION .s16code]
3.3 页式存储总结
分段机制将逻辑地址转换成线性地址,线性地址通过分页机制转换成物理地址。
为什么使用分页?(干货)
分页管理机制的目的在于实现虚拟存储器,线性地址中的任意一个页都能映射到物理地址中的任何一个页,使得内存管理机制特别灵活。
分页和分段的最大不同之处在于?(干货)
- 1)段的长度不固定: 段的长度通常与存放在其中的代码和数据结构具有相同的长度;
- 2)页面长度固定:页面具有固定的长度;
- 3)如果仅使用分段地址转换:那么存储在物理内存中的一个数据结构将包含所有的部分;
- 4)如果使用了分页,那么一个数据结构就可以一部分存储在物理内存中,一部分存储在磁盘上(虚拟存储器);
- 5)为了减少地址转换所要求的总线总起数量,最近访问的页目录和页表会被存放在处理器的缓冲器件中,该缓冲器件被称为TLB(translation lookaside buffer,翻译后备缓冲寄存器),提高访存效率;
3.3.1 分页机制概述
Why-为什么使用两极页表结构?
- w1)页表含有2^20(1M)个表项,每项占4字节。如果用一个表来存储的话,将最多占用4M;为减少内存占用量,X86使用了两极页表;
w2)每个,仅有一个页目录占用4k, 每个页表占用4k,而要知道页目录一定常驻内存,而页表是在需要的时候才占用内容空间,当然常用的页表会存储在TLB中,这在一定程度上减少了
页表机制进行线性地址与物理地址映射所占用内存空间;(干货)1)第一级页表——页目录(bit31~22):页目录项存储的内容是页表的基地址(高20位存储页表基地址,低12位存储所指向的页表属性)(干货); 它被存放在1页4k页面中, 含有1k个4字节长度的表项,通过线性地址的bit31~22进行索引页目录表项;
2)第二级页表——页表(bit21~12):页表项存储的内容是物理页的基地址(高20位存储物理页基地址,低12位存储物理页属性)(干货); 它被存放在1页4k页面中,含有1k个4字节长度的表项,而该页表的表项由 bit21~12进行定位;
3)偏移地址(bit11~0):它存储的是物理页的第12位地址(干货); 把页表项存储的高20位地址作为物理页的高20位地址,而偏移地址12位作为物理页的低12位地址,这样就得到了32位的物理地址;
- 4)映射到的物理地址空间:页长4k,总共有1k个页目录项,每个页目录项映射一张页表,每张页表有1k个页表项;故总共1k * 1k =1M个页表项;所以物理地址空间=1M * 4k = 4G
(you should know):
Y1)页目录的基地址存储在cr3中;
Y2)开启分页机制,要设置cr0的最高位PG位=1;
- 分页机制总结
- 内存分页机制总结
- 虚拟内存的分页机制一点总结(机制及倒排页表)
- 分页机制
- 分页机制
- 分页机制
- 分页机制
- 分页机制
- 分页机制
- 分页机制
- 分页机制
- 分页机制
- 分页机制
- 保护模式总结(四)——分页机制
- 保护模式总结(四)——分页机制
- 分页总结
- 分页总结
- 分页---总结
- shiro +springmvc无权限跳转到指定错误页面
- 运算符重载详解
- iOS开发中常见的语句@synthesize obj=_obj的意义详解
- 内联函数与虚函数
- C++ 中指针与引用的区别
- 分页机制总结
- MySQL事务隔离级别详解
- 线性结构和非线性结构
- C++ 友元函数
- C 结构体位域
- 同步函数与异步函数
- 阻塞与非阻塞,同步与异步
- C 运算符优先级
- quick 3.3加载Spine问题,quick3.3加载spine