LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 5266|回复: 23

内核页表的建立

[复制链接]
发表于 2004-12-25 12:30:05 | 显示全部楼层 |阅读模式
看了内核不久,对vm很感兴趣。比较很多的内核书籍,都没有看懂内核是怎么建立内核页表的,其实还有很多问题,比如:每个进程有自己的页表吗?我指相对内核页表而言,即需要另外开辟内存存放吗?书中反覆说内核页表建立的两个步骤,还是不大明白,希望高手们指点哈!
另外,能简单的描述一下内核1g空间是怎么分布的更好,因为我是比较糊涂的,很多东西绞在一起,互相矛盾了,真不好意思。
发表于 2004-12-25 22:12:42 | 显示全部楼层
看了一段时间VM了,说一说自己的理解,希望高手不要见笑了。
1。每个进程都有自己的页表的,需要存放在内存中,也可以交换出去,有个PRESENT位来标志是否在内存中。
2。你所说的两个步骤应该是指内核在初始化期间的吧???我说一下2。2内核页表初始化的两个步骤吧(映射前4M),2。4(映射前8M),2。6都是类似的。
内核在编译的过程中会静态初始化页目录和页表。页全局目录放在swapper_pa_dir中,页表放在pg0中。在初始化期间,要经历从实模式到保护模式的过渡,也就是说要开启分页功能。内核空间位于3G以上,用户空间在0--3G。由于最初cpu的IP寄存器中装的是物理地址,而编译得到的内核都是虚拟地址,目标是要内核映象在虚拟内核空间中运行,所以第一阶段首先将内核空间,用户空间(从0开始)都映射到物理地址前4M,可以对这4M寻址,对32位机器采用两级分页,通常页表,页目录分别利用虚拟地址10位来寻址,这样页表可以包含1024项,页目录也是1024项。内核空间的页目录项就是从第768项开始的(PAGE_OFFSET=0xC000 0000,内核空间1G,(1023-768+1)×1024×4K=1G),内核把页目录的第0项,第768(分别对应用户空间,内核空间第一项)项初始化为pg0的地址,其余项都初始化为0。这就建立了临时的内核页表,也就是第一阶段,arch/i386/kerne/head.s中的startup_32()函数利用向cr3寄存器写入swapper_pa_dir地址,并设置cr0寄存器的PG标志开启分页,然后以一个符号地址做绝对转移,由于这个符号地址是虚拟地址,于是跳转后就进入虚拟空间运行了,至此第一步完成,进入保护模式,这一阶段主要是为开启分页,进入保护模式;第二步是建立正式的内核页目录。首先把第一项清为了0,取消前4M线性地址和物理地址的映射,因为内核线程在运行的时候不允许通过用户空间虚拟地址访问内存,接着按照BIOS探测的物理内存的大小,建立映射,把整个物理内存都映射到内核地址空间。即内核空间0xC000 0000对应物理地址0。
 楼主| 发表于 2004-12-26 11:32:47 | 显示全部楼层
谢谢你得回复,我还有一个问题不明白。既然每个进程都有自己得页表目录,在进程运行得时候,也是使用的这个目录,只是在中断处理的时候,跳转到目录的高(1024-768)条目,也就是映射的内核页表目录的高1G条目执行,那内核页表目录的前768项是用来做什么的呢?映射物理内存?不懂
发表于 2004-12-26 12:04:31 | 显示全部楼层
前面的都填充为0,不起作用的,我前面帖子也提到了,内核线程不能通过用户空间的虚拟地址来访问内存,所以前面的用户空间的项都为0。
分配给每个进程一段线性地址,也只是把页目录项中相应部分初始化,其余项都是0。访问这些项,都会引发异常。
发表于 2004-12-26 12:23:07 | 显示全部楼层
你可以这样理解:内核在0环,普通进程在3环,只要普通进程操作合法,那内核就不能干涉它。也就是说不能在进程合法运行期间,强行通过自己页目录项中用户空间映射的虚拟地址来访问普通进程的空间。只有当发生异常或系统调用,也就是进程被迫或主动要求内核替它干某些事情,这才可以。若进程要求内核访问该进程地址空间的量,也会把这个做为参数传递给系统调用。
 楼主| 发表于 2004-12-26 15:38:07 | 显示全部楼层
我想我没有把我的意思表达清楚哈。我是说内核页表和进程自己的页表到底是什么一个关系?内核页表的低765项一直都是0吗?这些低项到底有什么用,怎么用?说实话,我越来越糊涂了,还麻烦您讲清楚一点哈,谢谢了!
发表于 2004-12-26 20:10:54 | 显示全部楼层
二级分页的话,IA-32把高十位做为页目录索引,中间10位是页表索引,最后12位是OFFSET;那么内核页目录的前768项相当于占位作用,一直是0。比如我找页目录中第800项,那么首先从CR3寄存器找到页目录所在地址,然后找索引为800的项;这些低项就是占着个位置,否则因为是0就删除这些低项的话就无法按照高10位索引找到正确的项。
每个进程都有自己的页目录,页表,即自己的地址空间;也可能进程之间某个页表相同,这意味着共享这块地址区域。
系统运行在内核态,那么需要对内核空间寻址,那么就利用内核页目录,页表寻址;但是对于需要访问某进程地址空间的情况(比如发生系统调用,给该系统调用传递了一个进程地址空间的参数),那么处于内核态的例程就可以访问该进程地址空间的页目录,页表,从而实现寻址。
但是进程不可以访问内核地址空间,比如如果发生系统调用,给该系统调用传递了一个进程地址空间的参数,而这个参数是在PAGE_OFFSET以上的,那就会引发异常。只有给内核传递PAGE_OFFSET以下的地址参数才合法。
应该说清楚了吧:内核寻址内核空间的时候(>=PAGE_OFFSET),那肯定要用内核页目录,页表了;需要寻址进程地址空间(<AGE_OFFSET),就利用进程页目录,页表。处于用户地址空间的进程是无权直接访问内核空间的,也就无法使用内核页表了。只有通过系统调用,进入内核态执行,这时候才可以访问内核页表。
发表于 2004-12-27 10:22:10 | 显示全部楼层
谢谢threeseconds精彩详尽的回复,精华
 楼主| 发表于 2004-12-27 10:46:50 | 显示全部楼层
感谢大侠的回帖,我明白了,原来内核也表的低768项没有使用,一直是0,只是起了一个占位检验正确与否的作用,可以这样理解吧,再次感谢哈!
发表于 2004-12-27 13:53:53 | 显示全部楼层
to westroom:
对,是这样。
CPU在用虚拟地址的高10位当作索引来查找页目录项时,是从页目录第一项开始,这10位索引当作相对于第一项的偏移,所以不能因为前面项是0就删除他们,他们需要占着位置,以便可以查找到正确的项;
另外,还有一点就是,内核线程是比较特殊的,它只可以在内核空间执行,不会由于调度发生上下文切换到用户空间,由于内核线程也要使用内核页目录,页表,因此把前面项设置为0,当内核线程利用前面项来访问内存的时候,就会引发异常从而发现错误。

我也是菜鸟,有问题大家可以一起讨论,有错误也要请大家指出:)
另外还要多谢版大抬举啊!!!!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表