当前位置:网站首页>虚拟内存与物理内存之间的关系
虚拟内存与物理内存之间的关系
2022-08-01 21:08:00 【是蜗牛骑士丫】
操作系统有虚拟内存与物理内存的概念。在很久以前,还没有虚拟内存概念的时候,程序寻址用的都是物理地址。程序能寻址的范围是有限的,这取决于CPU的地址线条数。比如在32位平台下,寻址的范围是2^32也就是4G。并且这是固定的,如果没有虚拟内存,且每次开启一个进程都给4G的物理内存.
就可能会出现很多问题:
- 因为我的物理内存时有限的,当有多个进程要执行的时候,都要给4G内存,很显然你内存小一点,这很快就分配完了,于是没有得到分配资源的进程就只能等待。当一个进程执行完了以后,再将等待的进程装入内存。这种频繁的装入内存的操作是很没效率的.
- 由于指令都是直接访问物理内存的,那么我这个进程就可以修改其他进程的数据,甚至会修改内核地址空间的数据,这是我们不想看到的.
- 因为内存时随机分配的,所以程序运行的地址也是不正确的.
一个进程运行时都会得到4G的虚拟内存。这个虚拟内存你可以认为,每个进程都认为自己拥有4G的空间,这只是每个进程认为的,但是实际上,在虚拟内存对应的物理内存上,可能只对应的一点点的物理内存,实际用了多少内存,就会对应多少物理内存。
进程得到的这4G虚拟内存是一个连续的地址空间(这也只是进程认为),而实际上,它通常是被分隔成多个物理内存碎片,还有一部分存储在外部磁盘存储器上,在需要时进行数据交换。
进程开始要访问一个地址,它可能会经历下面的过程:
- 每次我要访问地址空间上的某一个地址,都需要把地址翻译为实际物理内存地址.
- 所有进程共享这整一块物理内存,每个进程只把自己目前需要的虚拟地址空间映射到物理内存上.
- 进程需要知道哪些地址空间上的数据在物理内存上,哪些不在(可能这部分存储在磁盘上),还有在物理内存上的哪里,这就需要通过页表来记录.
- 页表的每一个表项分两部分,第一部分记录此页是否在物理内存上,第二部分记录物理内存页的地址(如果在的话).
- 当进程访问某个虚拟地址的时候,就会先去看页表,如果发现对应的数据不在物理内存上,就会发生缺页异常.
- 缺页异常的处理过程,操作系统立即阻塞该进程,并将硬盘里对应的页换入内存,然后使该进程就绪,如果内存已经满了,没有空地方了,那就找一个页覆盖,至于具体覆盖的哪个页,就需要看操作系统的页面置换算法是怎么设计的了。
关于虚拟内存与物理内存的联系页表的工作原理如下图:
页表的工作原理如下图:
- 我们的cpu想访问虚拟地址所在的虚拟页(VP3),根据页表,找出页表中第三条的值.判断有效位。
如果有效位为1,DRMA缓存命中,根据物理页号,找到物理页当中的内容,返回。 - 若有效位为0,参数缺页异常,调用内核缺页异常处理程序。内核通过页面置换算法选择一个页面
作为被覆盖的页面,将该页的内容刷新到磁盘空间当中。然后把VP3映射的磁盘文件缓存到该物理
页上面。然后页表中第三条,有效位变成1,第二部分存储上了可以对应物理内存页的地址的内
容。 - 缺页异常处理完毕后,返回中断前的指令,重新执行,此时缓存命中,执行1。
- 将找到的内容映射到告诉缓存当中,CPU从告诉缓存中获取该值,结束。
再来总结一下虚拟内存是怎么工作的
当每个进程创建的时候,内核会为进程分配4G的虚拟内存,当进程还没有开始运行时,这只是一个内存布局。实际上并不立即就把虚拟内存对应位置的程序数据和代码(比如.text .data段)拷贝到物理内存中,只是建立好虚拟内存和磁盘文件之间的映射就好(叫做存储器映射)。这个时候数据和代码还是在磁盘上的。当运行到对应的程序时,进程去寻找页表,发现页表中地址没有存放在物理内存上,而是在磁盘上,于是发生缺页异常,于是将磁盘上的数据拷贝到物理内存中。
另外在进程运行过程中,要通过malloc来动态分配内存时,也只是分配了虚拟内存,即为这块虚拟内存对应的页表项做相应设置,当进程真正访问到此数据时,才引发缺页异常。可以认为虚拟空间都被映射到了磁盘空间中(事实上也是按需要映射到磁盘空间上,通过mmap,mmap是用来建立虚拟空间和磁盘空间的映射关系的)。
利用虚拟内存机制的优点
- 既然每个进程的内存空间都是一致而且固定的(32位平台下都是4G),所以链接器在链接可执行文件时,可以设定内存地址,而不用去管这些数据最终实际内存地址,这交给内核来完成映射关系。
- 当不同的进程使用同一段代码时,比如库文件的代码,在物理内存中可以只存储一份这样的代码,不同进程只要将自己的虚拟内存映射过去就好了,这样可以节省物理内存。
- 在程序需要分配连续空间的时候,只需要在虚拟内存分配连续空间,而不需要物理内存时连续的,实际上,往往物理内存都是断断续续的内存碎片。这样就可以有效地利用我们的物理内存。
边栏推荐
猜你喜欢

98. Embedded controller EC actual combat EC development board development completed

Postman 批量测试接口详细教程

(七)《数电》——CMOS与TTL门电路

JS hoisting: how to break the chain of Promise calls

StringTable Detailed String Pool Performance Tuning String Concatenation

How to choose Visibility, Display, and Opacity when interacting or animating

C语言之字符串函数二

LeetCode·32.最长有效括号·栈·动态规划

关于Request复用的那点破事儿。研究明白了,给你汇报一下。

Transplant MQTT source code to STM32F407 development board
随机推荐
Pytorch框架学习记录8——最大池化的使用
C语言_typedef和结构体
R语言 线性回归的有关方法
附录A printf、varargs与stdarg A.2 使用varargs.h来实现可变参数列表
测试开发人均年薪30w+?软件测试工程师如何进阶拿到高薪?
织梦通过数据库查询调用当前文章的留言
Kubernetes 如何实现组件高可用
Postman 批量测试接口详细教程
tiup mirror
wps excel 插入公式 整列
深拷贝浅拷贝
PyTorch笔记 - Attention Is All You Need (2)
2022年秋招,软件测试开发最全面试攻略,吃透16个技术栈
tiup mirror grant
【中文树库标记---CTB】
Pytorch框架学习记录12——完整的模型训练套路
idea实用快捷键合集——持续更新
property语法
函数(二)
位运算简介