-
虚拟内存(为什么要有虚拟内存)
2020-07-11 15:28:35在很久以前,还没有虚拟内存概念的时候,程序寻址用的都是物理地址。程序能寻址的范围是有限的,这取决于CPU的地址线条数。比如在32位平台下,寻址的范围是2^32也就是4G。并且这是固定的,如果没有虚拟内存,且每次...操作系统有虚拟内存与物理内存的概念。在很久以前,还没有虚拟内存概念的时候,程序寻址用的都是物理地址。程序能寻址的范围是有限的,这取决于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),所以链接器在链接可执行文件时,可以设定内存地址,而不用去管这些数据最终实际内存地址,这交给内核来完成映射关系
当不同的进程使用同一段代码时,比如库文件的代码,在物理内存中可以只存储一份这样的代码,不同进程只要将自己的虚拟内存映射过去就好了,这样可以节省物理内存
在程序需要分配连续空间的时候,只需要在虚拟内存分配连续空间,而不需要物理内存时连续的,实际上,往往物理内存都是断断续续的内存碎片。这样就可以有效地利用我们的物理内存
————————————————
版权声明:本文为CSDN博主「TLpigff」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lvyibin890/article/details/82217193 -
malloc函数连续用两次,为什么分配的内存空间有重叠
2016-08-19 09:17:33我用malloc初始化了两个顺序链表,为什么这两个链表的首地址差只有16而不是1600呢,打印出来的结果是这样的,而且我在输入其中一个链表时另外一个的内存空间会被覆盖。 1600 2050472048 2050472064 -
为什么要有虚拟内存
2020-05-18 18:05:35没有有效的内存管理机制时,直接使用物理地址有以下三个问题: 1. 地址空间不隔离: 物理地址是连续的,直接使用物理地址,所有程序使用的内存在一个空间内,当一个程序内存溢出,很容易影响其他《程序员的自我修养》第一章14页对这部分讲的就很清楚,建议有需要的去看一下,我这里简单做下总结和笔记:
引用一句大佬的话:
“计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决”
那么直接用物理地址出现了什么问题?而怎加了虚拟地址这个中间件为什么可以解决它们?问题引出:
没有有效的内存管理机制时,直接使用物理地址有以下三个问题:
1. 地址空间不隔离:
物理地址是连续的,直接使用物理地址,所有程序使用的内存在一个空间内,当一个程序内存溢出,很容易影响其他程序的正常运行,导致其他程序崩溃。
2.内存使用效率低:
假设计算机有129M内存,已经有两个程序在运行,程序A需要10M,程序B需要100M,当程序C需要20M,执行时,调入内存,由于程序需要的空间是连续的,所以直接换出A可能释放空间不够,只能换出B,过程中有大量数据在内存和磁盘间交互,效率并不高。
3.程序运行地址不确定:
程序编写时,很多时候访问数据和指令跳转的目标地址是固定的,而直接使用物理地址,不能保证每次装入都放在同一块区域,就给程序编写带来了一定的麻烦。问题解决:
针对问题1:
虚拟地址保证每个进程都有自己独立的虚拟空间,而且进程空间只能访问自己的地址空间,这样就做到了进程隔离。
针对问题3:
以程序A为例,虚拟空间总是0到100M的连续空间,可以以分段的形式一对一的映射到物理空间上一段100M的空间内,这样即使每次分配的物理空间不固定,但程序看到的虚拟空间地址是固定的,大大方便了编程。
针对问题2:
解决效率的方法,是分页,我们把进程的虚拟空间按页分割,叫虚拟页,常用的装载到内存中,不常用的代码和数据保存到磁盘里,需要的时候再读入,相应的物理内存分页叫物理页,两者映射,可以大大增加内存的使用效率。ps:CPU发出的和,程序看到的都是虚拟地址,需要一个叫MMU(在cpu内部)的部件做映射,转化成物理地址。
-
打印内存地址
2014-02-20 20:26:57问题:请问在printf打印函数中一般用什么形式打印变量的内存地址? 解析:这个题目是C语言编程中应用十分普遍的一个基础知识。最常用的打印方式有%p和%x两种。代码如下: #include int main() { int a; ...问题:请问在printf打印函数中一般用什么形式打印变量的内存地址?
解析:这个题目是C语言编程中应用十分普遍的一个基础知识。最常用的打印方式有%p和%x两种。代码如下:
#include<stdio.h> int main() { int a; printf("%p\n",&a); printf("0x%X\n",&a); getchar(); }
上述代码的输出结果:
0057F84C
0x57F84C
总结:
1. %p和%x的区别:
%p:打印的地址上自动在地址前加入0x前缀;
%x:仅仅是用十六进制打印,并不会自动加0x。
-
Python的变量内存地址和C/C++的变量内存地址
2019-01-19 22:16:52突然想到Python的可变类型和不可变类型,在复习数据结构与算法时,用的C++,想看一下两种语言的变量地址有什么不同 在Python中,可以发现不可变类型,比如int类型,元组,当给变量赋值不同的值时,变量的地址会变化...突然想到Python的可变类型和不可变类型,在复习数据结构与算法时,用的C++,想看一下两种语言的变量地址有什么不同
在Python中,可以发现不可变类型,比如int类型,元组,当给变量赋值不同的值时,变量的地址会变化,这也说明这些变量在Python的管理器(也不知道叫啥,虚拟机?管理器?)中是缓存的。如下代码:
In [1]: a=1 In [2]: id(a) Out[2]: 140706035789568 In [3]: a=2 In [4]: id(a) Out[4]: 140706035789600 In [5]: type(a) Out[5]: int In [6]: b=(1,2) In [7]: id(b) Out[7]: 1932103472136 In [8]: b=(2,3) In [9]: id(b) Out[9]: 1932103502728
再补充一下,下图中a,b都为1,可以发现 a,b的地址一样,所以python中的不可变元素也是提前缓存的,和C/C++的指针地址比较就能悟出其中的道理。
在C/C++中,变量的值改变,地址并不改变,也使我更加理解了指针这个东西:
#include<iostream> using namespace std; int main(){ int i=0; cout<<&i<<endl; i=2; cout<<&i<<endl; }
输出以下结果:
[Running] cd "d:\WorkPlace\VCSWorkPlace\c_c++\" && g++ test.cpp -o test && "d:\WorkPlace\VCSWorkPlace\c_c++\"test 0x6dfefc 0x6dfefc
-
java 变量地址_Java中变量的内存地址
2021-02-12 16:56:53当我们用java创建一个带有new关键字的对象时,我们从OS获取一个内存地址。当我们写out.println(objName)时,我们可以看到一个"特殊"字符串作为输出。我的问题是:这是什么输出?如果它是操作系统给我们的内存地址:a... -
新睿云服务器增加虚拟内存的应该如何操作?有什么用?玩游戏有用吗?失败应该怎么办?
2019-11-21 14:31:51就像虚拟内存一样也是可以增加的,当您使用新睿云服务器发现内存不够影响用户体验的时候此时就可以按照本教程内容进行增加虚拟内存操作! 一、新睿云服务器如何升级虚拟内存? 1.首先要打开新睿云官网,官网地址... -
为什么要用虚拟地址?
2020-07-28 12:49:43通过虚拟地址访问内存有以下优势: 1 程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓冲区。...一个进程在自己的虚拟地址空间 3G中随便访问,都不至于影响到其它进程的内存地址。 一旦有访问非 -
内存地址空间与十六进制,为什么需要八进制和十六进制? 十六进制的意义何在
2016-05-03 11:28:02众所周知, 内存地址空间是用16进制的数据表示, 如0x8049324, 那为什么需要用十六进制表示呢? 十六进制的意义何在? 编程中,我们常用的还是10进制.毕竟C/C++是高级语言。 比如:int a = 100,b = 99; ... -
为什么32位的计算机最多只能用4G的内存
2011-05-21 16:44:26为什么32位的计算机最多只能用4G的内存呢?因为32位的计算机只有32根地址线,每个地址线有两个状态,所以32位计算机的寻址能力是2*32=4G。... -
为什么内存为什么是以字节为单位的?
2019-09-18 08:09:12我们以一个最简单的问题开篇,为什么内存地址0x0001和内存地址0x0002之间差的是一个byte,而不是一个bit? 我想你对内存存储的计算已经熟练于心,a变量占用内存多少字节,b变量占用内存多少字节。但是你有没有深入... -
地址线和数据线(为什么用字节来计量存储容量)
2016-05-04 16:47:19地址线是用来传输地址信息用的。举个简单的例子:cpu在内存或硬盘里面寻找一个数据时,先通过地址线找到地址,然后再通过数据线将数据取出来。 如果有32根.就可以访问2的32次方的空间,也就是4GB。 也就是说,... -
js闭包函数为什么有内存泄漏的问题存在
2016-06-28 20:13:40可以认为一个引用类型的变量就是一个指向某个具体的内存地址的指针。 当我们用js代码创建一个引用类型的时候(以下简称对象),js引擎会在内存中开辟一块空间来存放数据,并把指针引用交给那个变量。 -
在小内存芯片中为什么要用unsigned int unsigned char这个数型
2013-01-23 23:26:00内存与地址我们都见过像这样挂在墙上的很多个邮箱,每个邮箱有一个房间编号。邮箱的地址使用时根据房间编号找到相应的邮箱,然后投入信件或取出信件。内存与此类似,每个存储单元有一个地址(Address),CPU通过地址... -
句柄详解,什么是句柄?句柄有什么用?
2018-10-25 19:44:23最近在做项目时遇到了句柄的概念,之前只知道在编译原理里有句柄的概念,在实际的编程时会经常用到句柄。...2.图中一个小横框表示一定大小的内存区域,并不代表一个字节,如标有0X00000AC6的横框表示4... -
final有什么用
2018-01-25 23:00:091、final修饰变量:表示该变量一旦被初始化值之后就不可能被改变。... c、如果是引用类型,其引用地址不会发生变化,但该对象的堆内存的值可以发生变化。 public final class Test { public static final Strin -
hashCode到底有什么用?
2018-07-23 15:47:44hashCode到底有什么用? hashCode概念 hashCode是jdk根据对象的地址算出来的一个int数字,即对象的哈希码值,代表了该对象在内存中的存储位置。 我们都知道hashCode()方法是顶级类Object类的提供的一个... -
指针和地址到底有什么区别
2020-02-13 17:54:16看了很多博客,有的博客总把指针和地址混为一谈,我谈下我的看法,仅供自己复习用。 定义上讲,指针(变量)里放的是地址,这就是他们之间的联系。 a[ ]是一个指针数组,char*是数组a的元素类型,本来应该放的是... -
内存中为什么分堆和栈,能否只用一种模型呢?为什么每个线程都有单独的栈
2020-12-24 23:23:38在学编程的时候,我们应该都听过一句话 “如果程序结束之后仍然想要访问那一段数据就要用堆(不释放的话,程序修改后的数据仍存在)”,我想这个其实就是本题目的关键了,堆和栈都有其自己的独特性,可能你了解这两... -
内存地址空间与十六进制,为什么需要八进制和十六进制? 十六进制的意义何在?...
2011-10-17 17:20:00众所周知, 内存地址空间是用16进制的数据表示, 如0x8049324, 那为什么需要用十六进制表示呢? 十六进制的意义何在? 编程中,我们常用的还是10进制.毕竟C/C++是高级语言。 比如:int a = 100,b = 99; 不过,... -
Java内存——new对象的时候内存中都发生了什么
2018-04-17 15:36:41new 对象时内存变化:Student s = new Student();1. 加载Student类文件到栈内存,开辟空间。... 如果成员变量有给定值则用给定值覆盖默认值6. 通过构造方法给成员变量赋值7. 把Student对象在堆内存的地址赋给s变量... -
关于c语言内存地址对齐的一点思考
2014-05-18 16:29:54至于为什么要使用内存对齐,这是一个比较复杂的问题,简单来说就是提高cpu access memory的性能,后续有时间就内存对齐这个问题,展开详细的探讨。示例首先来看一个简单的示例:假设我们现在要用c语言做一个简单的... -
服务器地址_服务器、IP地址和域名有什么关系呢?
2021-01-15 05:16:05很多朋友应该都听过服务器、ip以及域名这些名词,但是它们到底是什么,能作什么用,可能一些朋友就不太清楚了,今天56云就来给大家介绍下服务器、IP地址和域名有什么关系呢?一、服务器服务器其实就像我们的家用电脑... -
字符指针为什么输出的是地址_为什么C语言中的字符串可以用指针表示?
2021-01-29 21:07:49为什么很多人不爱学C语言那?...就需要用到内存地址,内存地址是一个十六进制的数字,比如说0x403024这就可以表示一个变量,拿这个东西也可以访问变量,但是如果变量都长这个样子,那程序员可就惨了,所以... -
动态数组——指针和动态内存分配函数联用
2020-10-22 16:52:52我终于决定要写一些关于专业知识的博客了,被自己感动到了。...用于分配若干字节的内存空间,返回一个指向该内存首地址的指针。 void *malloc(unsigned int size) 函数调用成功将返回一个指向void类型的指针 -
字节偏移量是什么_没内存了还能看片?什么是虚拟内存?虚拟内存原理与工作方式...
2020-12-06 01:42:25虚拟内存尽管基址寄存器和变址寄存器用来创建地址空间的抽象,但是这有一个其他的问题需要解决:管理软件的膨胀(managing bloatware)。虽然内存的大小增长迅速,但是软件的大小增长的要比内存还要快。在 1980 年的... -
内存中有两个4字节以压缩的bcd_内存的物理机制
2020-12-12 12:24:43课前问答1. 有十个地址信号引脚的内存 IC(集成电路)可以指定的地址范围是多少?每个引脚可以看成一位,每个位有0/1两个状态,210=102个地址用二进制... 在 32 位内存地址的环境中,指针变量的长度是多少位?32位指针...