6.1 虚拟内存
6.1.1 背景
前面介绍的太详细了,导致这个虚拟内存一下子不知道怎么引入了,尴尬。
我们在前面介绍,基本涵盖了整个内存管理的演变:
- 从一开始整个程序加载到0地址,然后就可以执行了,这种方法虽然简单,但是只能运行单道程序。
- 然后进行分区,把整个内存进行分区,然后不同程序加载到不同分区,只需要一个重定位寄存器就可以运行不同分区的程序。缺点就是容易造成内存外部碎片,也可能导致大程序运行不了。
- 然后进行分段,分段是把整个程序按照不同的属性,进行不同的分段,数据段、代码段栈段等等。这样做的优点就是可以把整个程序拆开,以适应不同段加载进内存。
- 分段虽然好,但是不太适用于物理内存,因为即使分段也会存在外部碎片,所以对于物理内存我们使用了分页处理,就是在段的基础上再次细分,这样造成的碎片至多也就一页。
经过上面的演变,在根据我们之前说的程序的局限性原理,相对于程序部分比较适合分段,而对物理内存比较适合分页。
那如果是这样的话,我们程序执行代码的时候,怎么把分段和分页结合起来,其实我们在分段的时候就已经讲过了:
一个程序我们保存有段表、页表,程序的地址先通过段号去找到段表,找到对应的页表,再通过页表去查到对应的物理地址。这种做法我不知道有没有具体使用,不过从理论上是可行的。
不过这种玩法不是我们今天说的虚拟内存。虚拟内存是一个更抽象的概念,是在这个上面做了一些抽象。
虚拟内存是把整个计算机的逻辑内存就行整体抽象,如果这个计算机是32位,那就抽象成一个4G大小的虚拟内存,然后我们程序中的各个段,会分散的各个部分,对应这不同的地址,程序执行的时候,会根据这些虚拟地址,查找到页表,通过页表计算出对应的物理地址,当然这部分不是软件计算,是一个叫**MMU(内存管理单元)**这个硬件计算。(这个是我个人理解,等之后分析源码的时候,再求证对不对。)
6.1.2 虚拟内存介绍
一个进程有4G的虚拟地址,我们写代码的大小小于4G,并且在这4G虚拟内存中,把我们代码分为不同的段,不同段经过编译,形成了自己的虚拟地址,如果我们执行代码的话,就通过虚拟地址发送给MMU,由MMU自动转化(其实就是查找页表)成物理地址,然后我们就可以操作物理地址了。
关于虚拟地址的具体实现,期待我们源码篇,我们会直接分析源码,到目前为止我们的理解也就是这样了。
6.1.3 共享页
既然都讲到这部分了,那共享页肯定会出现了,由于我们使用的是虚拟内存,所以实现共享页很简单,看看下面的图就可以理解了:
共享页其实很简单,只要每个进程的页表中,关于这个共享页指向同一个物理页就可以了,我们通过虚拟地址去查找的时候,就会映射成同一页。
进程的共享内存,就是这种方式实现的,我们会在源码篇分析的,期待吧。
6.2 总结
这一篇写的比较短,是因为有前面5篇做了铺垫,还有就是这一篇要想证明,还需要分析源码,所以我们尽量结束原理篇,正是进入源码篇,我们来看看linux是怎么实现内存管理的。加油