每天花5分钟,了解linux内存相关的知识点

刚开始学习c语言的时候,会接触到一个概念,就是虚拟内存,当时真的是一头雾水,根本不知道什么是虚拟内存,我为此感到好无奈,随着对c语言的深入理解,慢慢的,我开始明白了虚拟内存的概念,今天我就讲讲我对虚拟内存的理解,鄙人如有哪里不对的,请指正。

虚拟内存的由来

我们知道cpu的速度非常快,外设相对于cpu又很慢,这时就需要一个中介来缓解这种差异,这个中介是谁呢?那就是我们常说的内存。

现代的操作系统都是多任务的,那么多个任务同时工作,就会有内存访问的冲突,那么如何解决这种冲突呢?虚拟内存就是解决这种冲突的。

假设有两个进程1和2,操作系统给进程1和2进程一种假象,操作系统告诉1和2,我的整个内存都是你的,你们随便用,管饱,而事实上,操作系统让他们随便用的只是虚拟内存,当1和2开始使用内存的时候,系统才根据他们的虚拟地址为他们分配物理内存,也就是说,是动态分配的。

进程1和2认为他们使用的虚拟地址就是物理地址,但是事实上,linux内核为他们的虚拟地址做了一个转换,转换成了物理地址。

那么虚拟内存是如何与物理内存进行转换的呢?

linux内核的做法是,利用页目录表进行的映射。

Linux 虚拟地址与物理地址之间的映射

每天花5分钟,了解linux内存相关的知识点

图1

上图中的VA代表虚拟地址,PA代表物理地址,通过上图我们知道,我们的程序将虚拟地址传递给CPU,CPU会根据MMU将虚拟地址进行映射,也就是虚拟地址通过MMU部件后,输出的结果就是PA,也就是物理地址。

MMU 是根据什么机制映射的物理地址呢?

MMU是根据页目录表和页表进行虚拟地址与物理地址的映射的,我们只要把虚拟地址告诉MMU,MMU会主动去查页目录表以及页表,最终找到所需要的物理地址。

MMU它也不是神仙,知道我们的页目录表存在内存的什么位置,所以MMU并不会直接去内存查找页目录,而是通过寄存器CR3来知道页目录的基地址,换句话说,页目录的基地址存在了CR3的寄存器中,MMU读取CR3的寄存器就知道了页目录的基地址,是不是觉得明白了许多。

具体的映射关系如下所示:

每天花5分钟,了解linux内存相关的知识点

图2

上图就是虚拟地址到物理地址的映射过程。

线性地址你可以认为是虚拟地址就行,因为现在的linux内存模型基本上是平坦模型,也就是段的基地址从0开始,所以虚拟地址和线性地址是相等的关系。

下面我就根据上图,来详细说明映射的过程:

1. 首先将线性地址分成三份,这三份分别占用了 22-31位,12-21位,0-11 位。

22-31 位表示的是页目录的索引,页目录你可以认为就是一个数组,这个索引代表数组的索引,MMU会根据这个索引,来选取页目录数组的哪个数组元素。

12-21 位表示的是页表的索引,页表也可以认为是一个数组,MMU会根据这个索引,来选取页表数组中的哪个数组元素。

0-11 位表示的页面的偏移,当MMU通过页目录表和页表知道了页面的基地址后,会用页面的基地址 加上 页面的偏移来算出真正的物理地址。

2. MMU 去读取CR3的寄存器的值,获取页目录的基地址。

3. 获取完页目录的基地址后,会根据线性地址中的22-31位的值,来获取页目录的索引,即为pgd_offset,然后通过基地址加上 4*pgd_offset 后,获取到了页目录项。为什么乘以4呢?是因为 页目录数组中的元素占用4个字节,所以 用 4*pgd_offset 才能获取到页目录项。

4. 获取完页目录项后,我们就知道了页表的基地址,因为页目录项中存放的是页表的基地址,虚拟地址中的12-21 位表示的是页表的索引,我们设为pg_offset ,然后,通过页表的基地址 加上 4*pg_offset 后,就得出了页面的物理地址。这里乘以4,也是因为页表项占用4个字节。

5. 我们得出了页面的物理地址,已经成功了一大半了,最后根据虚拟地址中的0-11 位得到页面的偏移,即为p_offset,然后通过页面的物理地址加上 p_offset后,形成了最终的物理地址。

以上就是虚拟地址到物理地址之间的映射流程,是不是感觉对虚拟地址有了进一步的理解了。

可能有的人会问,是谁往CR3寄存器了存的页目录的基地址?每个进程它的页目录表一样吗?预知后事,请听下回分解,请大家对我进行关注,让我觉得写的东西对有些人是有帮助的,谢谢。

请大家关注我哦

每天花5分钟,了解linux内存相关的知识点

图3


分享到:


相關文章: