http://blog.csdn.net/gxfan/article/details/3173292
linux内核地址空间与用户地址空间的差别
本文仅限在i386平台下讨论一般情况。
1、用户线性地址空间范围0-3G,内核线性空间范围3G-4G。
2、内核总是立即满足内核空间的物理内存分配,并且分配结果对所有进程可见;而对于用户空间的内存分配请求,linux总是先保留用户线性地址空间的一段区域,然后修改页表项使这段线性区域都指向一页内容全为0的全局只读物理页。当进程写入这段线性区域时,将会产生一个缺页异常,这时系统才会为对应的线性地址分配物理页面,并且把物理页对应的页表项置为可写。为了保持和全局只读物理页内容一致,新分配的物理页内容也会全置为0。
3、用户线性空间是不可靠的,一般情况下它会随着进程的切换而改变(在lazy TLB切换下除外);而内核线性空间对所有的进程都一样,不会随着进程的切换而改变
http://blog.csdn.net/gxfan/article/details/3122489
以下讨论仅限 i386 平台,一般考虑典型情况
-
linux 内核对整个系统的物理内存是通过类型为 struct page 的数组 mem_map 来管理的。系统中的伙伴系统分配算法最终是通过操作这个数组来记录物理内存的分配、回收等操作。在这里不要被系统的高端内存、低端内存等概念搞混淆了,高、低端内存的分类主要在于区分物理内存地址是否可以直接映射到 内核线性地址空间 中。
我们知道, linux 的内核地址空间大小为 1G (用户空间 0~3G ,内核空间 3G~4G ,这种分法最常见),因此如果把这 1G 线性地址空间全部拿来 直接一一映射 物理内存的话,在内核态的所有进程(线程)能使用的物理内存总共最多只有 1G, 为了能使在内核态的所有进程能使用更多的物理内存, linux 采取了一种变通的形式:它将 1G 内核线性地址空间分为几部分,第一部分为 1G 的前 896M ,这部分内核线性空间与物理内存的 0~896M 一一映射(相差一个为 0xc0000000 的常数),后面 128M 的线性空间拿来动态映射剩下的所有物理内存,由于动态映射的方法不一样,后面的 128M 又分成了几个部分,有兴趣的可以查看相关资料。在这里,前面 896M 线性空间对应的物理内存就是所谓的低端物理内存,剩下的物理内存就是高端物理内存。
从上面高、低端物理内存命名的由来我们可以知道,高、低端物理内存与具体的内存分配算法无关,它们都是被 mem_map 数组控制起来,再由伙伴分配系统实施管理。
-
关于进程及其内存分配
首先要明白一个概念:进程中使用的所有地址都是虚地址,在 linux 下这个虚地址就是所谓的线性地址。 linux 中进程可运行在用户态和内核态,(典型配置情况下)当进程运行在用户态时,它使用的线性地址只能位于 0~3G 范围内,当进程运行于内核态时,它使用的线性地址地址范围为 3G~4G 。
为了把线性地址转化为物理地址,每个进程都有自己私有的页目录和页表。 linux 在建立进程页目录时,把用户地址空间的页目录项( 0~767 项)清空而将内核页目录表( swapper_pg_dir )的第 768 项到 1023 项拷贝到进程的页目录表的第 768 项到 1023 项中。由于内核在初始化时也只映射了物理内存的前 896M ,我们可以知道内核也目录表只能保证第 768 项开始的 224 项中有有效映射。 从这里我们可以知道,所有的进程都共享了其内核线性地址空间 。
当一个进程在内核空间发生缺页故障的时候,这主要发生在访问内核空间动态映射区线性地址,在其处理程序中,就要通过 0 号进程的页目录( swapper_pg_dir )来同步本进程的内核页目录,实际上就是拷贝 0 号进程的内核页目录到本进程中(内核页表与进程 0 共享,故不需要复制)。如果进程 0 的该地址处的内核页目录也不存在,则出错,具体代码可以参考 vmalloc 的实现源码。
当进程运行于用户态时,若其需要申请内存空间,内核首先会在其用户线性空间中分配需要的线性地址空间,再通过伙伴分配系统分配物理内存并把分配的物理内存跟用户空间线性地址映射起来,最后再修改进程的页目录项及页表项写入这些映射关系。