转:http://blog.csdn.net/h_armony/article/details/9842831
分页与分段最大的不同之处在于分页是用来固定长度的页面(一般为4KB)。如果仅适用分段地址转换,那么存储在物理内存中的一个数据结构将包含器所有部分。但如果适用了分页,那么一个数据结构就可以一部分存储在物理内存中,而另一部分保存在磁盘中。这也就是分页机制在现代内存管理中存在的重要性。
那么首先我们还是熟悉一下分页机制的基本概念。
如上图所示,分页是在分段的基础上将线性地址转化为物理地址。转换时使用了两级页表,第一级是页目录,大小为4KB,存储在一个物理页上,每个表项4字节长,共有1024个表项。每个表项对应第二级的一个页表,第二级的每个页表也有1024个表项,每个表项对应一个物理页。页目录表的表项简称PDE(Page Directory Entry),页表的表项简称PTE(Page Table Entry)。
整个转换过程:
1)获取页目录表项:从CR3中的高20位获取页目录所在的物理地址,再将线性地址的高10位作为索引找到页目录表项;
2)获取页表项:从页目录表项中高20位获取页表所在的物理地址,再将线性地址的中间10位作为索引找到页表项;
3)获取物理地址:从页表项中高20位获取页所在的物理地址,再加上线性地址的低12位,便得到了物理地址;
注:1)在32位X86中,上述高20位在使用过程中,首先需要左移12位,构成32位地址;
2)启动页机制:将CR0的第31位置1;
CR3、PDE以及PTE结构(自P2055)
下面我们还是通过分析代码来理解分页:
- PageDirBase equ 200000h ; 页目录开始地址:2M
- PageTblBase equ 201000h ; 页表开始地址: 2M+4K
- ; 启动分页机制 --------------------------------------------------------------
- SetupPaging:
- ; 为简化处理, 所有线性地址对应相等的物理地址.
- ; 首先初始化页目录
- mov ax, SelectorPageDir ; 此段首地址为 PageDirBase 200000h(2M)
- mov es, ax ; es:[edi]指向PageDirBase,页目录表基地址
- mov ecx, 1024 ; 共 1K 个表项,循环1024次
- xor edi, edi
- xor eax, eax
- mov eax, PageTblBase | PG_P | PG_USU | PG_RWW ; eax里是PageTblBase(201000h)加上属性PG_P | PG_USU | PG_RWW后的结果,也就是第一个页表的地址和属性
- .1:
- stosd ; es:[edi] ← eax ; 把第一个页表地址、属性填入页目录基地址,也就是第一个页目录表项里
- add eax, 4096 ; 为了简化, 所有页表在内存中是连续的. ;eax里存的其实就是页目录表项的内容,这里在第一个表项内容基础上加上4096,相当与第二个表项指向的页
- 表基地址是PageTblBase+4096,下面是循环以此类推...
- loop .1 ;循环1024次,完成页目录里1024个表项的初始化
- ; 再初始化所有页表 (1K 个, 4M 内存空间)
- mov ax, SelectorPageTbl ; 此段首地址为 PageTblBase
- mov es, ax ; es:[edi]指向PageTblBase,页表基地址
- mov ecx, 1024 * 1024 ; 1024个页表,每个页表有1024个表项, 也就是有 1M 个页
- xor edi, edi
- xor eax, eax
- mov eax, PG_P | PG_USU | PG_RWW ; 这里属性前没有地址,其实地址是0,也就是将线性地址0开始的4K空间映射到物理地址上
- .2:
- stosd ; es:[edi] ← eax ; 把第一个页表地址、属性填入页表基地址,也就是第一个页表里
- add eax, 4096 ; 下一页表里存放的基地址在前一个的基地址基础上+4049,实际就是表明一个页的大小为4K
- loop .2 ; 循环1M次,每次映射1页(4K大小的空间),所以总共映射了4G空间
- mov eax, PageDirBase
- mov cr3, eax ;cr3寄存器里存入PageDirBase基地址
- mov eax, cr0
- or eax, 80000000h
- mov cr0, eax ;将控制寄存器cr0的PE标志(位31)置为1,开启分页机制
- jmp short .3
- .3:
- nop
- ret
- ; 分页机制启动完毕 ----------------------------------------------------------
上述程序大致做了这样的事:构建页目录表和页表,为了构建简单,表项都在内存中连续存放,将页目录表基地址存入cr3中,然后开启分页机制。
关键的地方是在页目录表和页表结构里的基地址内容:
PDE里页表基址:依次为201000,202000......600000。总共1024个,每两个页表基地址相差4K,这是由于一个页表基址指向的是1024个页表的基地址,而每个页表4个字节。
PTE里页基址:依次为0000 0000,0000 01000......FFFF FFFF。总共1024*1024个,没两个页基址相差4K,这是由于一个页面占4K。这样就将4G的地址空间全都映射到了。
分页的图示:
书中还提到一句话 "切换任务时通过改变cr3的值来切换页目录,从而改变地址映射关系”,这应该是切换进程时要做的事吧。