操作系统开发系列—6.内存分页机制
a.概述
页尺寸是4KB,页表每个表项占4字节,CR3寄存器给出了页目录的物理基地址;页目录给出了所有页表的物理地址,而每个页表给出了它所包含的页的物理地址。
处理器的页部件专门负责线性地址到物理地址的转换工作。它首先将段部件送来的32位线性地址截成3段,分别是高10位、中间的10位和低12位。高10位是页目录的索引,中间10位是页表的索引,低12位则作为页内偏移来用。
假如某个任务加载后,操作系统根据它的实际情况在其4GB虚拟地址空间里创建了一个段,段的起始地址为0x00800000,段界限值为0x5000,字节粒度。当该任务执行时,段寄存器DS指向该段。又假设执行了下面一条指令:
mov edx,[0x1050]
此时,段部件会输出线性地址0x00801050。在没有开启分页机制时,这就是要访问的物理内存地址,但现在开启了分页机制,这是一个虚拟地址,要经过页部件的转换才能得到物理地址。
当前任务页目录的物理地址在处理器的CR3寄存器中,假设它的内容为0x00005000.段管理部件输出的线性地址是0x00801050,其二进制的形式为0000 0000 1000 0000 0001 0000 0101 0000.高10位为0000000010,也就是十六进制的0x002,它是页目录表内的索引,处理器将它乘以4(因为每个目录项为4字节),作为偏移量访问页目录。最终,处理器从物理地址00005008处取得页表的物理地址0x08001000.
页目录表的表项简称PDE,页表的表项简称PTE。分页机制是否生效的开关位于cr0的最高位PG位。如果PG=1,则分页机制生效。
b.源码
PageDirBase equ 200000h ; 页目录开始地址: 2M PageTblBase equ 201000h ; 页表开始地址: 2M+4K 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 SelectorPageDir equ LABEL_DESC_PAGE_DIR - LABEL_GDT SelectorPageTbl equ LABEL_DESC_PAGE_TBL - LABEL_GDT ; 启动分页机制 -------------------------------------------------------------- SetupPaging: ...
PageDirBase和PageTblBase指定了页目录表和页表在内存中的位置。页目录表位于地址2MB处,有1024个表项,占用4KB空间。
PDE(左)和PTE(右)格式如下:
cr3的结构如下图,cr3又叫PDBR,它的高20位将是页目录表首地址的高20位,页目录表首地址的低12位会是零,也就是说,页目录表会是4KB对齐的。类似的,PDE中的页表基址以及PTE中的页基址也是用高20位来表示4KB对齐的页表和页。
运行结果如下:
d.分页机制的作用
LinearAddrDemo equ 00401000h ProcFoo equ 00401000h ProcBar equ 00501000h ProcPagingDemo equ 00301000h call SetupPaging ; 启动分页 call SelectorFlatC:ProcPagingDemo call PSwitch ; 切换页目录,改变地址映射关系,和SetupPaging代码差不多 call SelectorFlatC:ProcPagingDemo PagingDemoProc: OffsetPagingDemoProc equ PagingDemoProc - $$ mov eax, LinearAddrDemo call eax retf LenPagingDemoAll equ $ - PagingDemoProc foo: OffsetFoo equ foo - $$ mov ah, 0Ch ; 0000: 黑底 1100: 红字 mov al, 'F' mov [gs:((80 * 17 + 0) * 2)], ax ; 屏幕第 17 行, 第 0 列。 mov al, 'o' mov [gs:((80 * 17 + 1) * 2)], ax ; 屏幕第 17 行, 第 1 列。 mov [gs:((80 * 17 + 2) * 2)], ax ; 屏幕第 17 行, 第 2 列。 ret LenFoo equ $ - foo bar: OffsetBar equ bar - $$ mov ah, 0Ch ; 0000: 黑底 1100: 红字 mov al, 'B' mov [gs:((80 * 18 + 0) * 2)], ax ; 屏幕第 18 行, 第 0 列。 mov al, 'a' mov [gs:((80 * 18 + 1) * 2)], ax ; 屏幕第 18 行, 第 1 列。 mov al, 'r' mov [gs:((80 * 18 + 2) * 2)], ax ; 屏幕第 18 行, 第 2 列。 ret LenBar equ $ - bar
ProcFoo的值处的地址对应的是foo:函数,ProcBar的值处的地址对应的是bar:函数。PSwitch和SetupPaging差不多,只是紧接着程序增加了改变线性地址LinearAddrDemo对应的物理地址的语句。改变后,LinearAddrDemo将不再对应ProcFoo,而是对应ProcBar。切换的过程是通过改变cr3的值完成的。
【源码】