Linux 0.11中的页目录表及页表内容分析

Linux0.11针对的内存是16M。采用了两级分页机制进行内存的管理。

根据head.s中第114行的.org 0x1000可知,物理地址0x1000之前的所有数据都将被页目录表覆盖(这个覆盖,是指更改了内存中的内核镜像文件,而不是磁盘上的内核镜像文件)。

1、首先,Linux从0x00000地址开始对五页内存进行清零。(1页页目录表+4页页表)

    stosl store EAX at address ES:(E)DI;EDI+=4。stosl指令相当于将eax中的值保存到 ES:(E)DI指向的地址中。

   setup_paging: 
       movl $1024*5,%ecx        /* 5 pages - pg_dir+4 page tables */ 
       xorl %eax,%eax 
       xorl %edi,%edi            /* pg_dir is at 0x000 */ 
       cld;rep;stosl

2、接着,填写页目录表(页目录表的位置为0x00000-0x00fff,大小为4K,每一项占4字节)。因为只有4个页表,所以只填写了前四项。

1       movl $pg0+7,pg_dir        /* set present bit/user r/w */ 
2       movl $pg1+7,pg_dir+4        /*  --------- " " --------- */ 
3       movl $pg2+7,pg_dir+8        /*  --------- " " --------- */ 
4       movl $pg3+7,pg_dir+12        /*  --------- " " --------- */ 

这里,4个页目录项的内容分别是$pg0(1,2,3)+7,分别是4个页表的物理地址+111B。前面的$pg0(1,2,3)是页表的物理地址,而111B则代表这4个页表权限为可读写。

3、填写页表项的内容

1       movl $pg3+4092,%edi 
2       movl $0xfff007,%eax        /*  16Mb - 4096 + 7 (r/w user,p) */ 
3       std 
4   1:    stosl            /* fill pages backwards - more efficient :-) */ 
5       subl $0x1000,%eax 
6       jge 1b 

这里的填写是逆序填写的,也就是首先将16M物理内存最后一页的启始地址+权限(16M-4K+111B)填写到第4张页表的最后一项。地址为$pg3+4092,其中$pg3为(第4张页表的起始地址),4092是因为1024*4(1024项,每项占用4字节)-4(最后一项页占用4字节)。所以第4张页表的最后一项是(16M-4K)0xfff000+111B=0xfff007。

最终,std以4递减edi寄存器(一个页表项占4字节,edi指向正在操作的页表项),subl $0x1000,%eax将减去0x1000(一页内存的大小,eax指向正在操作的内存边界),l:stosl是填写页表项,知道eax的内容为0,这样就填写完了4个页表。

这样,内存中的页目录表和页表分布就是:

物理内存地址 所含信息及备注 单元内容
0X00FF FFFC
……
  ……
……
0X0000 4FFC
0X0000 4FF8


0X0000 4004
0X0000 4000
页表4
4K (0X00004000-0X00004FFC)
4字节为一项
0X00FF F000+7
0X00FF E000+7


0X00C0 2000+7
0X00C0 1000+7
0X00C0 0000+7
0X0000 3FFC
0X0000 3FF8


0X0000 3004
0X0000 3000
页表3
4K (0X00003000-0X00003FFC)
4字节为一项
0X00BF F000+7
0X00BF E000+7


0X0080 2000+7
0X0080 1000+7
0X0080 0000+7
0X0000 2FFC
0X0000 2FF8


0X0000 2004
0X0000 2000
页表2
4K (0X00002000-0X00002FFC)
4字节为一项
0X007F F000+7
0X007F E000+7


0X0040 2000+7
0X0040 1000+7
0X0040 0000+7
0X0000 1FFC
0X0000 1FF8


0X0000 1004
0X0000 1000
页表1
4K (0X00001000-0X00001FFC)
4字节为一项
0X003F F000+7
0X003F E000+7


0X0000 2000+7
0X0000 1000+7
0X0000 0000+7
0X0000 0FFC
0X0000 0FF8


0X0000 0004
0X0000 0000
PDT(页目录表)
4K (0X000000-0X00000FFF)
只有前四项有内容

0x0000 4000+7
0x0000 3000+7
0x0000 2000+7
0x0000 1000+7

最后,把页目录表的地址(0x000000)写到控制寄存器CR3,然后置控制寄存器CR0的PG位,这样就开启了内存的分页管理功能

Linux0.11在分页机制下的寻址(两级表寻址)

第一级表称为页目录。

存放在1页4k页面中。具有1k个4字节长度的表项。这些表项指向第二级表。线性地址的最高10位(位31-22)用作一级表(页目录)中的索引值来选择某个页目录项,用以选择某个二级表。

第二级表称为页表。

长度也是1个页面,每个表含有1k个4字节的表项。每个4字节表项含有相关页面的20位物理地址。二级页表使用线性地址中间的10位(位21-12)作为表项索引值,在表内索引含有页面20位物理地址的表项。该20位页面物理地址和线性地址中的低12位(页内偏移)组合在一起就得到了分页转换中的输出值,也就是最终的物理地址。

也就是说:

线性地址高10位---------索引页目录表----------->找到相应页表

线性地址中间10位---------索引页表----------->得到页表中相应的项,其中的高20位就是物理地址的高20位

线性地址低12位-------------------->物理地址的低12位

无标题

下面举两个例子说明Linux0.11的分页寻址。

1、寻址0x38

首先写成32位地址为0x0000 0038。取最高10位,0000 0000 00(2进制),索引页目录项。这里找到页目录表的第0项,内容为$pg0+7=0x0000 1007。取线性地址的中间10位,00 0000 0000(2进制),索引页表1(pg0)中的第0项,内容为

0x0000 0000+7,取他的高20位0x0000 0加上线性地址的低12位0x038就得到最终的物理地址0x0000 0038

2、寻址0x00f5 9f50

首先取最高10位,0000 0000 11(2进制),索引页目录表。这里为3就找到页目录表的第3项,指向页表4,内容为$pg3+7=0x0000 4000+7。取线性地址的中间10位,11 0101 1001B,索引页表4(pg3)中的0x359项,内容为0x00f5 9000+7。取其高20位0x00f5 9000加上线性地址的低12位0xf50就得到最终的物理地址0x00f5 9f50

posted on 2011-07-27 13:47  不知道  阅读(2942)  评论(1编辑  收藏  举报

导航