一个操作系统的实现(4):分页机制

来源:互联网 发布:莫宁职业生涯数据 编辑:程序博客网 时间:2024/05/21 00:19

 分页与分段最大的不同之处在于分页是用来固定长度的页面(一般为4KB)。如果仅适用分段地址转换,那么存储在物理内存中的一个数据结构将包含器所有部分。但如果适用了分页,那么一个数据结构就可以一部分存储在物理内存中,而另一部分保存在磁盘中。这也就是分页机制在现代内存管理中存在的重要性。

那么首先我们还是熟悉一下分页机制的基本概念。


如上图所示,分页是在分段的基础上将线性地址转化为物理地址。转换时使用了两级页表,第一级是页目录,大小为4KB,存储在一个物理页上,每个表项4字节长,共有1024个表项。每个表项对应第二级的一个页表,第二级的每个页表也有1024个表项,每个表项对应一个物理页。页目录表的表项简称PDE(Page Directory Entry),页表的表项简称PTE(Page Table Entry)。

整个转换过程:

1)先是从寄存器cr3指定的页目录中根据线性地址的高10位得到页表。

        2)在页表中,根据线性地址的第12—21位得到物理页首地址。

        3)将这个首地址,加上线性地址的低12位便得到了物理地址。

PDE的结构图如下:


PTE的结构图如下:


cr3的结构图如下:



下面我们还是通过分析代码来理解分页:

[cpp] view plain copy
  1. PageDirBase  equ 200000h ; 页目录开始地址:2M  
  2. PageTblBase  equ 201000h ; 页表开始地址: 2M+4K  
  3.   
  4. ; 启动分页机制 --------------------------------------------------------------  
  5. SetupPaging:  
  6.     ; 为简化处理, 所有线性地址对应相等的物理地址.  
  7.   
  8.     ; 首先初始化页目录  
  9.     mov ax, SelectorPageDir ; 此段首地址为 PageDirBase  200000h(2M)                             
  10.     mov es, ax          ; es:[edi]指向PageDirBase,页目录表基地址                                       
  11.     mov ecx, 1024       ; 共 1K 个表项,循环1024次                                    
  12.     xor edi, edi  
  13.     xor eax, eax  
  14.     mov eax, PageTblBase | PG_P  | PG_USU | PG_RWW  ; eax里是PageTblBase(201000h)加上属性PG_P  | PG_USU | PG_RWW后的结果,也就是第一个页表的地址和属性                 
  15.   
  16.               
  17. .1:  
  18.     stosd               ; es:[edi] ← eax    ; 把第一个页表地址、属性填入页目录基地址,也就是第一个页目录表项里                            
  19.   
  20.       
  21.     add eax, 4096       ; 为了简化, 所有页表在内存中是连续的.   ;eax里存的其实就是页目录表项的内容,这里在第一个表项内容基础上加上4096,相当与第二个表项指向的页  
  22.   
  23.                                           表基地址是PageTblBase+4096,下面是循环以此类推...                      
  24.     loop    .1                      ;循环1024次,完成页目录里1024个表项的初始化  
  25.   
  26.     ; 再初始化所有页表 (1K 个, 4M 内存空间)                                        
  27.     mov ax, SelectorPageTbl ; 此段首地址为 PageTblBase  
  28.     mov es, ax                  ; es:[edi]指向PageTblBase,页表基地址  
  29.     mov ecx, 1024 * 1024    ; 1024个页表,每个页表有1024个表项, 也就是有 1M 个页  
  30.     xor edi, edi  
  31.     xor eax, eax  
  32.     mov eax, PG_P  | PG_USU | PG_RWW                     ; 这里属性前没有地址,其实地址是0,也就是将线性地址0开始的4K空间映射到物理地址上  
  33. .2:  
  34.     stosd                           ; es:[edi] ← eax    ; 把第一个页表地址、属性填入页表基地址,也就是第一个页表里  
  35.     add eax, 4096       ; 下一页表里存放的基地址在前一个的基地址基础上+4049,实际就是表明一个页的大小为4K  
  36.     loop    .2          ; 循环1M次,每次映射1页(4K大小的空间),所以总共映射了4G空间                                       
  37.   
  38.     mov eax, PageDirBase                                              
  39.     mov cr3, eax        ;cr3寄存器里存入PageDirBase基地址                                          
  40.     mov eax, cr0                                                  
  41.     or  eax, 80000000h  
  42.     mov cr0, eax            ;将控制寄存器cr0的PE标志(位31)置为1,开启分页机制                                            
  43.     jmp short .3  
  44. .3:  
  45.     nop  
  46.   
  47.     ret  
  48. ; 分页机制启动完毕 ----------------------------------------------------------  

上述程序大致做了这样的事:构建页目录表和页表,为了构建简单,表项都在内存中连续存放,将页目录表基地址存入cr3中,然后开启分页机制。

关键的地方是在页目录表和页表结构里的基地址内容:

PDE里页表基址:依次为201000,202000......600000。总共1024个,每两个页表基地址相差4K,这是由于一个页表基址指向的是1024个页表的基地址,而每个页表4个字节。

PTE里页基址:依次为0000 0000,0000 01000......FFFF FFFF。总共1024*1024个,没两个页基址相差4K,这是由于一个页面占4K。这样就将4G的地址空间全都映射到了。


分页的图示:



书中还提到一句话 "切换任务时通过改变cr3的值来切换页目录,从而改变地址映射关系”,这应该是切换进程时要做的事吧。


0 0