物理地址和逻辑地址
- 物理地址
主存储器的存储单元以字节(每个字节为8个二进制位)为单位编址,每个存储单元都有一个地址与其相对应。假定主存储器的容量为n,则该主存就有n个字节的存储空间,其地址编号为0,1,2…,n-1。这些地址称为主存储器的“物理地址”(绝对地址),由物理地址所对应的主存空间称“物理地址空间”。 - 逻辑地址
在多道程序设计系统中,主存中同时存放了多个用户作业。每次调入运行时,操作系统将根据主存的使用情况为用户分配主存空间。
为了方便程序的编制,每个用户可以认为自己作业的程序和数据存放在一组以“0”地址开始的连续空间中。用户程序中所使用的地址称为“逻辑地址”,由逻辑地址对应的存储空间称为“逻辑地址空间”。
程序的装入
将一个用户的源程序装入主存中并执行,通常需要经过以下几个步骤:
- 编译,由编译程序(Compiler)将用户源代码编译成若干个目标模块(Object Module)。
- 链接,由链接程序(Linker)将编译后形成的一组目标模块以及它们所需要的库函数链接在一起,形成一个完整的装入模块(Load Module)。
- 装入,由装入程序(Loader)将装入模块装入主存。
程序的装入方式
- 绝对装入方式
如果在编译时知道程序驻留在主存的具体位置,则编译程序将产生物理地址的目标代码。绝对装入程序按照装入模块中的地址,将程序和数据装入主存。模块装入后,由于程序中的逻辑地址与实际主存的地址完全相同,所以不需要对程序和数据的地址进行修改。
绝对装入方式只能将目标模块装入到主存储器事先指定的固定位置,它只适用于单道程序环境。 - 静态重定位装入方式
在装入作业时,把该作业中的指令地址和数据地址一次性全部转换成物理地址,这样在作业执行过程中无需再进行地址转换工作,这种地址转换方式称“静态重定位”。而这种作业装入的方式称为“静态重定位装入方式”。
- 动态重定位装入方式
在装入作业时,装入程序直接把作业装入到所分配的主存区域中。
在作业执行过程中,随着每条指令或数据的访问由硬件地址转换机制自动地将指令中的逻辑地址转换成对应的物理地址。
这种地址转换的方式是在作业执行过程中动态完成的,故称为“动态重定位”。而这种作业装入的方式称为“动态重定位装入方式”。
为何会有虚拟内存
操作系统有虚拟内存与物理内存的概念。在很久以前,还没有虚拟内存概念的时候,程序寻址用的都是物理地址。程序能寻址的范围是有限的,这取决于CPU的地址线条数。比如在32位平台下,寻址的范围是2^32也就是4G。并且这是固定的,如果没有虚拟内存,且每次开启一个进程都给4G的物理内存,就可能会出现很多问题:
- 因为我的物理内存时有限的,当有多个进程要执行的时候,都要给4G内存,很显然你内存小一点,这很快就分配完了,于是没有得到分配资源的进程就只能等待。当一个进程执行完了以后,再将等待的进程装入内存。这种频繁的装入内存的操作是很没效率的
- 由于指令都是直接访问物理内存的,那么我这个进程就可以修改其他进程的数据,甚至会修改内核地址空间的数据,这是我们不想看到的
- 因为内存时随机分配的,所以程序运行的地址也是不正确的。
进程访问一个地址的过程
- 每次我要访问地址空间上的某一个地址,都需要把地址翻译为实际物理内存地址
- 所有进程共享这整一块物理内存,每个进程只把自己目前需要的虚拟地址空间映射到物理内存上
- 进程需要知道哪些地址空间上的数据在物理内存上,哪些不在(可能这部分存储在磁盘上),还有在物理内存上的哪里,这就需要通过页表来记录
- 页表的每一个表项分两部分,第一部分记录此页是否在物理内存上,第二部分记录物理内存页的地址(如果在的话)
- 当进程访问某个虚拟地址的时候,就会先去看页表,如果发现对应的数据不在物理内存上,就会发生缺页异常
- 缺页异常的处理过程,操作系统立即阻塞该进程,并将硬盘里对应的页换入内存,然后使该进程就绪,如果内存已经满了,没有空地方了,那就找一个页覆盖,至于具体覆盖的哪个页,就需要看操作系统的页面置换算法是怎么设计的了。
虚拟内存与物理内存的联系
页表的工作原理
- 我们的cpu想访问虚拟地址所在的虚拟页(VP3),根据页表,找出页表中第三条的值.判断有效位。 如果有效位为1,DRMA缓存命中,根据物理页号,找到物理页当中的内容,返回。
- 若有效位为0,参数缺页异常,调用内核缺页异常处理程序。内核通过页面置换算法选择一个页面作为被覆盖的页面,将该页的内容刷新到磁盘空间当中。然后把VP3映射的磁盘文件缓存到该物理页上面。然后页表中第三条,有效位变成1,第二部分存储上了可以对应物理内存页的地址的内容。
- 缺页异常处理完毕后,返回中断前的指令,重新执行,此时缓存命中,执行1。
- 将找到的内容映射到告诉缓存当中,CPU从告诉缓存中获取该值,结束。
虚拟内存是怎么工作的
当每个进程创建的时候,内核会为进程分配4G的虚拟内存,当进程还没有开始运行时,这只是一个内存布局。实际上并不立即就把虚拟内存对应位置的程序数据和代码(比如.text .data段)拷贝到物理内存中,只是建立好虚拟内存和磁盘文件之间的映射就好(叫做存储器映射)。这个时候数据和代码还是在磁盘上的。当运行到对应的程序时,进程去寻找页表,发现页表中地址没有存放在物理内存上,而是在磁盘上,于是发生缺页异常,于是将磁盘上的数据拷贝到物理内存中。
另外在进程运行过程中,要通过malloc来动态分配内存时,也只是分配了虚拟内存,即为这块虚拟内存对应的页表项做相应设置,当进程真正访问到此数据时,才引发缺页异常。
可以认为虚拟空间都被映射到了磁盘空间中(事实上也是按需要映射到磁盘空间上,通过mmap,mmap是用来建立虚拟空间和磁盘空间的映射关系的)
利用虚拟内存机制的优点
- 既然每个进程的内存空间都是一致而且固定的(32位平台下都是4G),所以链接器在链接可执行文件时,可以设定内存地址,而不用去管这些数据最终实际内存地址,这交给内核来完成映射关系。
- 当不同的进程使用同一段代码时,比如库文件的代码,在物理内存中可以只存储一份这样的代码,不同进程只要将自己的虚拟内存映射过去就好了,这样可以节省物理内存。
- 在程序需要分配连续空间的时候,只需要在虚拟内存分配连续空间,而不需要物理内存时连续的,实际上,往往物理内存都是断断续续的内存碎片。这样就可以有效地利用我们的物理内存。
部分参考并整理于:https://blog.csdn.net/lvyibin890/article/details/82217193