本帖最后由 Zoro_ 于 2017-9-29 16:18 编辑
前段时间一直在看linux内核的内容,读的是毛德操老师的《Linux内核源代码情景分析》,虽然书很老了(内核版本2.4),但是读下来感觉收获颇多,对linux的认识更加深刻了,以下总结我读存储管理的部分收获(第二遍,主要是第一遍读完啥感觉都没有)。希望大家看了这篇文章能有所收获,或者为大家读这本书列一些提纲。(以下内容均为2.4版本的内容,且标题命名与原书保持一致,本人才疏学浅还未研究新的内核)。
2.1 Linux内存管理的基本架构
首先我们看linux从线性地址到物理地址的映射:
内核机制采用3层映射机制,逻辑上把32位线性地址划分4个段,各段占有若干位,依次为PGD(全局页面目录),PMD(中间页面目录),PT(页表),以及物理页的偏移。
(1)以线性地址的最高位段为下标,在PGD中找到指向PMD的指针;
(2)以线性地址的次位段为下标,在PMD中找到指向PT的指针;
(3)以线性地址的第三位段为下标,在PT中找到指向页面的指针;
(4)线性地址的最后位段,为在此页中的偏移量,这样就完成了从线性地址到物理地址的映射过程。(如下图,图片来源于原书30页)
但是以上内容是Linux内存管理的机制,这是为了适应更多的CPU制定的机制,在i386上是怎么做的呢?
首先要看的是i386从线性地址(32位)到物理地址的映射过程:
(1)从CR3取得页面目录的基地址;
(2)以线性地址的高10位为下标,在目录中取得相应页面的基地址;
(3)以线性地址的中间10位为下标,在所得页面表中取得相应的页面描述项;
(4)将页面描述项给出的页面基地址与线性地址的最后12位相加得到物理地址。(如下图,图片源于原书13页)
由此我们清楚的看到,linux的三层映射在i386要变成二层,所以地址映射的过程就变成:
(1)内核为MMU设置好映射目录PGD,线性地址前10位作为下标在PGD中找到中间目录PMD;
(2)PMD只是软件上存在,MMU并不知道PMD的存在,表中只有一个表项,保持原值,直接指向页面表;
(3)内核为MMU设定好页面表,MMU从线性地址的中间10位为下标找到表项PTE;
(4)线性地址的最后12位作为偏移量加上刚才的表项基地址就得到物理地址了。
2.2 地址映射全过程
一种CPU支持页式存储管理,就不用再支持段式储存管理,但是由于i386发展的特殊原因,程序中先进行段式映射,然后才进行页式映射(这部分在我看来很难理解,想知道细节的可以自己看相关书),Linux内核是怎么做的呢?其实Linux设计的段式映射机制把最初的地址直接映射到自身,作为线性地址进行页式映射,于是如2.1中说的,高10位在目录中取得相应页面的基地址,中间10位为下标,在所得页面表中取得相应的页面描述项,最后12位作为偏移量得到具体地址。
最后我们聊一聊“数学”,所谓数学就是带大家算一算,有助于大家理解以上内容:
1.一个页面的大小为4K=4*1024,偏移量刚好为12位,也就是2的12次方刚好等于4K;
2.一个页面目录项或者一个目录项为32位(4个字节),一个目录项、一个页面表中有2的10次方,也就是1024个目录项,所以一个页面刚好可以放一个页面目录或页面表;
3.一个32位的CPU寻址能力为4G,一个页面大小为4k,前20位为1024*1024个表项,1024*1024*4k=4G;
4.页面表和页面的起始地址都是在4K的边界上,所以这些指针的低12位永远都是0,所以一个物理地址,把偏移的12位全换成0,就得到物理内存页面的起始地址。
剩下几节内容就全都是源码的解读了,关于这部分内容,之后我会分享关于数据结构和数据结构联系的小结和执行过程和函数调用的小结(当然这也是作者本人的建议……)。
关于这本书,共有上下俩本,我已经粗读了一遍了,但是大部分内容都没能懂,而且忘得很快……现在第二遍读,可以说是尽心尽力了……我也没接触其他书,反正感觉这本还不错,推荐(虽然很老)。