[本博客连载并且会持续修正,转载请注明出处:
http://blog.chinaunix.net/uid-26583089-id-5730535]
内核设置映射关系,MMU执行映射
80386地址映射过程:
Linux内核建立地址映射关系过程:
实例:虚拟地址0x01011000。
假设内核将这个虚拟地址按8:8:4:12划分为:PGD表项下标=1、PMD表项下标=1、PT下标=1、位移=0,依次创建PGD表、PMD表、PT,并建立联系。
对于80386,它会将这个地址按10:10:12划分为:目录项下标=4、PT下标=9、位移=0。
显然,内核在设置映射关系时,与硬件对线性地址的划分如果不一样,建立的映射关系就不是硬件需要的。
既然内核对线性地址的理解必须和硬件一致,为什么Linux内核对线性地址的划分还要比80386多一级?
Linux内核要保证在各种类型的CPU运行,假想Linux将线性地址固定划分为10:10:12,保证在80386执行没问题,但对于其它要求不同的CPU,就必须单独再写一份代码了。
那么Linux内核如何按3级的逻辑,建立2级的映射关系?
PGD、PMD、PT之间的界限,是“活动”的,针对不同的CPU,可以通过调整宏的值进行“移动”,可以是8:8:4:12,也可以是10:0:10:12,当“PMD_SHIFT=PGDIR_SHIF,PTRS_PER_PMD=1(理解为2^0或者1个确定的映射表项y=x)”时,实际就只会建立2级映射。不过,逻辑上仍然是3级,只不过到PMD这一级时,发现根本没必要创建PMD表,就啥也没做。
虚拟空间、物理空间、用户空间、内核空间
32位系统,每个进程拥有4GB“虚拟空间”,其中3~4GB属于“内核空间”,0~3G属于“用户空间”。虚拟空间的地址最终要映射到“物理空间”,即实际的内存地址。
虚拟空间、物理空间,举个例子:
一个戏剧剧本里写了1000个人物,人物的名字不妨就叫1、2、3 。。1000,这就相当于“虚拟地址”,只是用于描述一个故事或者一段逻辑的代号,不同的剧本里用相同的代号不会产生冲突。
要把剧本演出来,可能只需要30个人,同一名演员在戏剧进行到不同的时候,画不同的妆,就可以饰演戏剧中不同的人物,演员就相当于“物理地址”。
一名演员为什么可以演不同的人物?比如某个人物在第2集就挂了,那么演这个人物的演员,在第2集之后就可以演别的人物(内存释放);某个人物如果出场率不高,就第10、20集出现,那么演这个人物的演员,就可以在非10、20集时,演别的人物(交换分区)。
用户空间、内核空间,举个例子:
内核是诸葛亮、用户进程A是赵云、用户进程B是张飞,诸葛亮号令全军,赵云、张飞只能号令自己的部队,要不然赵云说:“前锋出战”,结果张飞家的前锋吭哧吭哧跑出去了,张飞再说:“前锋出战”时,前锋都不知道跑哪去了,军队都是玩命的,哪能这么随便是吧。
不过,确实有需要“借兵”的情况呀,那就得委托诸葛亮号,由诸葛亮根据情况将赵云或还没有编制(公用)的部队给他用一下,或者拒绝。
诸葛亮掌握的是全军的概要信息,相当于“内核空间”,赵云、张飞只知道自己部队的信息,但比诸葛亮知道的更详细,相当于各自的“用户空间”。用户态、内核态的划分,正是依赖于
Linux内核预备知识(2)所介绍的“权限划分+特权指令”。
注意!用户空间、内核空间是对虚拟空间的划分,物理空间并不存在这样划分,同一个物理地址,既可以映射给用户地址,也可以映射给内核地址。
然而,内核地址的映射规则“内核地址-3GB=物理地址”非常确定,即3~4GB内核空间一定一一映射到0~1GB物理地址,如果内存紧张,需要把0~1GB的某些物理地址映射给用户地址,内核会怎么做呢?
系统初始化时,会检查内存信息,并构建一个物理页面“仓库”。而所谓“分配”,不管内核自己为了“公共事业”需要分配,还是用户程序需要分配,都是由内核从内核空间或用户空间找到空闲的虚拟地址(“代号”),再从仓库“取出”相应数量的物理页面,并在内核或用户进程的页表里建立二者的映射关系。
当内存紧张需要把0~1GB的物理内存分配给用户进程时,内核先当作为“自己”分配,建立内核空间与这块物理空间的映射,但它并不用这块内存做“公共事业”,而是“送”给用户进程,即建立用户进程的虚拟地址与这块物理空间的映射。
所以不用担心“电脑上装的内存条如果小于1GB,连用户空间都没有,而不能执行用户程序”。
“公共的”内核空间
如何保证内核空间是公共的,即不管切换到哪个进程,都能“看见”这块空间?
答案是,每个进程的映射表里,都能找到已分配的内核地址与物理地址的映射关系。
内核空间分配、释放时,如何“同步”各个进程的映射信息?
比如执行进程A时进入内核态释放了一部分内核空间的内存,那么通过进程A的目录表、页表就找不到这个内核地址的映射关系了,此时如果切换到进程B,却还能找到这条映射关系,而进程B正好又进入内核态访问这个地址,那不就出现问题了吗?其实这里有一种类似“指针”的思想,1GB大小的地址空间,需要1MB的页表,内核在启动就分配好,并且再也不释放,真正的内容只有这一份,每个进程的目录项后1/4部分都指向这些页表,不管内核地址有没有分配,MMU都会找到页表这一层,页表中没有映射就表示没有分配。而不管哪个进程进入内核态,修改的都是这一份页表。
阅读(2586) | 评论(0) | 转发(0) |