在kern/pmap.c 里面会又下面这段代码,要知道boot_alloc仅仅会分配线性地址,真正建立虚拟页和物理页映射关系的在后面的page_alloc.
//////////////////////////////////////////////////////////////////////
// create initial page directory.
kern_pgdir = (pde_t *) boot_alloc(PGSIZE);
memset(kern_pgdir, 0, PGSIZE);
这里有个疑问,memset仅会接受虚拟地址,而这里boot_alloc分配出的kern_pgdir 是线性地址,这里还“没有建立起实际的物理映射”,怎么就能用memset去把kern_pgdir指向的地址出PGSIZE大小空间的数据全部填充为0.
前面说的话已经又双引号了,嘿嘿,说明这就是个假象,或者说我理解的不够透彻. 这里要感谢Eric eshyong,以及和我一起讨论问题的Essential
On C & Linux的道友.
上面代码部分还处于已经开启分页但是还没有建立起所有的分页映射. 为什么这样说,是因为之前JOS的kernel
作者手动静态的完成了部分内存的映射,而这部分内存就是物理内存的前4M(0x400000)
在kern/entrypgdir.c 里面
注意这里把虚拟地址的 [0,4M) [KERNBASE,KERNBASE + 4M) 两个区间都映射到同一物理地址区间[0,4M)
所谓的静态映射就是手动的...把一个个地址页面都分配好,如下....
Revisit the page table setup in kern/entry.S and kern/entrypgdir.c . Immediately after we turn on paging, EIP is still a low number (a little over 1MB). At what point do we transition to running at an EIP above
KERNBASE? What
makes it possible for us to continue executing at a low EIP between when we enable paging and when we begin running at an EIP above KERNBASE? Why is this transition necessary?
这里就相当于要回答这个问题,在刚刚开启分页的时候(entry.S 里面 cr0 的)
# Turn on paging. movl %cr0, %eax orl $(CR0_PE|CR0_PG|CR0_WP), %eax movl %eax, %cr0
紧接着此时EIP指令寄存器还指向地址的低空间(1M多一点点的地方)
不难看出jmp这行代码使得地址空间起了变化,分页机制开始作用
既然分页已经开启了,那么就应当把高地址的KERNBASE映射到物理地址上,之前其实就已经做好了,这里把虚拟地址的 [0,4M) [KERNBASE,KERNBASE + 4M) 两个区间都映射到同一物理地址区间[0,4M)的目的就在于不要让指令的寻址受到地址空间变化的影响.
(这段代码我反复给出,比较重要)
回到我们原来的问题
//////////////////////////////////////////////////////////////////////
// create initial page directory.
kern_pgdir = (pde_t *) boot_alloc(PGSIZE);
memset(kern_pgdir, 0, PGSIZE);
这里有个疑问,memset仅会接受虚拟地址,而这里boot_alloc分配出的kern_pgdir 是线性地址,这里还“没有建立起实际的物理映射”,怎么就能用memset去把kern_pgdir指向的地址出PGSIZE大小空间的数据全部填充为0.
在这一步的时候,boot_alloc确实是申请出的线性地址,但是注意!这部分地址早就被静态映射好了。此时的kern_pgdir 得到的是线性地址,然而它并不需要page_alloc来给它动态的分配实际的内存,因为之前已经分配好了.
memset接受的参数也是线性的(虚拟的)。
二零一四年 十月 摄于妙音寺前