Linux0.11针对的内存是16M。采用了两级分页机制进行内存的管理。
根据head.s中第114行的.org 0x1000可知,物理地址0x1000之前的所有数据都将被页目录表覆盖(这个覆盖,是指更改了内存中的内核镜像文件,而不是磁盘上的内核镜像文件)。
1、首先,Linux从0x00000地址开始对五页内存进行清零。(1页页目录表+4页页表)
1 setup_paging:
2 movl $10245,%ecx / 5 pages - pg_dir+4 page tables /
3 xorl %eax,%eax
4 xorl %edi,%edi / pg_dir is at 0x000 */
5 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
p
g
3
+
7
,
p
g
d
i
r
+
12
/
∗
−
−
−
−
−
−
−
−
−
"
"
−
−
−
−
−
−
−
−
−
∗
/
这里,
4
个页目录项的内容分别是
pg3+7,pg_dir+12 /* --------- " " --------- */ 这里,4个页目录项的内容分别是
pg3+7,pgdir+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
0
x
1000
,
6
j
g
e
1
b
这里的填写是逆序填写的,也就是首先将
16
M
物理内存最后一页的启始地址
+
权限
(
16
M
−
4
K
+
111
B
)
填写到第
4
张页表的最后一项。地址为
0x1000,%eax 6 jge 1b 这里的填写是逆序填写的,也就是首先将16M物理内存最后一页的启始地址+权限(16M-4K+111B)填写到第4张页表的最后一项。地址为
0x1000,6jge1b这里的填写是逆序填写的,也就是首先将16M物理内存最后一页的启始地址+权限(16M−4K+111B)填写到第4张页表的最后一项。地址为pg3+4092,其中$pg3为(第4张页表的起始地址),4092是因为10244(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个页表。
这样,内存中的页目录表和页表分布就是:
最后,把页目录表的地址(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 00B,索引页目录项。这里找到页目录表的第0项,内容为$pg0+7=0x0000 1007。取线性地址的中间10位,00 0000 0000B,索引页表1(pg0)中的第0项,内容为
0x0000 0000+7,取他的高20位0x0000 0加上线性地址的低12位0x038就得到最终的物理地址0x0000 0038
2、寻址0x00f5 9f50
首先取最高10位,0000 0000 11,索引页目录表。这里为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