精华内容
下载资源
问答
  • 虚拟地址空间
    千次阅读
    2021-08-04 10:29:42

    1. 虚拟地址空间

    1.1 定义

    虚拟存储器是一个抽象概念,它为每个进程提供了一个假象,好像每个进程都在独占地使用主存。每个进程看到的存储器都是一致的,称之为虛拟地址空间

    地址空间就是每个进程所能访问的内存地址范围,是一段连续的虚拟内存空间。
    在这里插入图片描述

    1.2 背景(为什么需要虚拟地址空间?)

    因为早期的计算机运行方式是直接将程序运行在物理内存上。这就存在三个问题:

    问题1. 地址空间不隔离
    	所有进程都直接访问物理地址,程序使用的物理空间不是隔离的
    	
    问题2. 运行效率低
    	监控程序直接将整个程序装入内存进行执行
    	
    问题3. 运行地址不确定
    	因为每次执行装入的物理地址是不确定的
    

    为了解决以上三个问题,增加了一个中间层,用来解决上面的问题。
    那就是给出一个虚拟地址,然后通过一种映射关系,将其与物理地址进行映射,将虚拟地址转换成物理地址。
    这样就能解决存在的问题1和问题3,问题2需要页映射来解决。

    这个可以看之前的博客介绍。
    https://blog.csdn.net/lqy971966/article/details/99533610

    1.3 大小/平坦地址空间

    每个进程都有一个32位或者64位的平坦(flat)地址空间,具体大小取决于体系结构。
    平坦指地址空间范围是一个独立的连续空间,如0~4294967295(2^32)。
    因为每个内存都有唯一的平坦地址空间,所以一个进程的地址空间与另一个进程的地址空间即使有相同的内存地址,实际上也彼此互不相干。

    1.4 内存区域

    1.4.1 内存区域定义

    尽管一个进程可以寻址4GB的虚拟内存,但这并不代表它有权访问所有的虚拟地址。

    可被访问的合法地址空间称为内存区域(memory areas)
    

    通过内核,进程可以给自己的地址空间动态地添加或减少内存区域。进程只能访问有效内存区域内的内存地址,每个内存区域也具有相关权限属性(可读、可写、可执行)。

    1.4.2 内存区域组成(进程地址空间)

    linux下,进程虚拟空间分为:操作系统占1GB,进程占3GB。
    linux下一个进程的地址空间从上到下依次是:

    内核空间:内核是操作系统总是驻留在存储器中的部分
    	应用程序不允许读写这个区域的内容或者直接调用内核代码定义的函数
    	
    栈:临时变量,返回值等,向低地址扩展
    	用户栈在程序执行期间可以动态地扩展和收缩
    	调用函数时,栈就会增长每次我们从函数返回时,栈就会收缩
    	
    共享库:存放像C标准库和数学库这样共享库的代码和数据的区域
    
    堆:动态分配的内存,向高地址扩展
    	调用像malc和free这样的C标准库函数的结果,堆可以在运态地扩展和收缩
    	
    bss段数据:即未初始化全局变量的内存映射
    
    数据段(只读数据段):即可执行文件的已初始化全局变量的内存映射
    
    代码段:即可执行文件二进制代码的内存映射(可执行文件映像)
    
    保留区域
    

    1.5 页表映射物理内存

    地址空间最终会通过页表映射到物理内存上,因为内核操作的是物理内存。

    1.5.1 局部性原理(动态装入的基本原理)

    程序运行时是有局部性原理的,所以可以将最常用的部分驻留内存中,其他放在硬盘里,这就是动态装入的基本原理。

    页:
    OS将内存和磁盘中的数据和指令按照页/page为单位划分成若干个页。
    以后所有的装载和操作的单位就是页。
    

    1.5.2 两个映射

    创建虚拟地址空间的本质是:创建映射函数需要的数据结构。

    映射1:
    建立虚拟空间和可执行文件的映射关系

    其实这个关系就是:发生页错误时,OS应该知道程序当前需要的页在可执行文件中的哪一个位置。
    
    映像文件(Image):
    因为可执行文件在被装载时是被映射到虚拟地址空间的,所以很多时候可执行文件又被叫做映像文件。
    这种映射关系也是保存在OS中的一个数据结构中的。
    
    虚拟内存区域(VMA: Virtual Memory Area):
    linux将进程虚拟空间中的一个段叫做虚拟内存区域。windows叫做虚拟段。
    

    映射2:
    建立虚拟空间的各个页映射到相应的物理地址的映射

    在这里插入图片描述
    自己画的,参考之前的博客:https://blog.csdn.net/lqy971966/article/details/106910442

    1.5.3 页错误

    定义:
    当cpu执行进程的某个页面时,发现他要访问的页(虚拟地址的页)没有在物理内存中,而导致的中断(页错误)。
    (一个可执行文件可能很大,放在磁盘上,由局部性原理一次只将其中一部分读进内存)

    解决:
    通过页交换将虚拟页映射至物理内存中。

    详细请参考之前的博客:页错误 Page Fault 详解
    https://blog.csdn.net/lqy971966/article/details/106910442

    更多相关内容
  • 虚拟地址空间和物理地址空间

    千次阅读 2022-02-21 23:45:48
    每个进程都会分配虚拟地址空间,在32位机器上,该地址空间为4G 。平时以虚拟形式存储,当应用程序使用虚拟地址访问内存时/a.out执行,mmu将虚拟地址转换为物理地址执行(正常文件存储在磁盘中) 段错误:内存访问...

    1.概念

    物理地址:物理地址空间是实在的存在于计算机中的一个实体,在每一台计算机中保持唯一独立性。我们可以称它为物理内存;如在32位的机器上,物理空间的大小理论上可以达到2^32字节(4GB),但如果实际装了512的内存,那么其物理地址真正的有效部分只有512MB = 512 * 1024 KB = 512 * 1024 * 1024 B(0x00000000~0x1fffffff)。其他部分是无效的。

    虚拟地址:虚拟地址并不真实存在于计算机中。每个进程都分配有自己的虚拟空间,而且只能访问自己被分配使用的空间。理论上,虚拟空间受物理内存大小的限制,如给有4GB内存,那么虚拟地址空间的地址范围就应该是0x00000000~0xFFFFFFFF。每个进程都有自己独立的虚拟地址空间。这样每个进程都能访问自己的地址空间,这样做到了有效的隔离。

    在上面我们提到了合理的内存管理机制。我们这里虚拟地址和物理地址之间的映射是通过MMU(内存管理单元)来完成的。
    在这里插入图片描述
    MMU:将虚拟的地址转化为物理地址。
    段错误:内存访问非法,内核空间受保护
    堆空间存储后一定要释放

    优点:

    1.既然每个进程的内存空间都是一致而且固定的,所以链接器在链接可执行文件时,可以设定内存地址,而不用去管这些数据最终实际的内存地址,这是有独立内存空间的好处。

    2.当不同的进程使用同样的代码时,比如库文件中的代码,物理内存中可以只存储一份这样的代码,不同的进程只需要把自己的虚拟内存映射过去就可以了,节省内存。

    3.在程序需要分配连续的内存空间的时候,只需要在虚拟内存空间分配连续空间,而不需要实际物理内存的连续空间,可以利用碎片。

    另外,事实上,在每个进程创建加载时,内核只是为进程“创建”了虚拟内存的布局,具体就是初始化进程控制表中内存相关的链表,实际上并不立即就把虚拟内存对应位置的程序数据和代码(比如.text .data段)拷贝到物理内存中,只是建立好虚拟内存和磁盘文件之间的映射就好(叫做存储器映射),等到运行到对应的程序时,才会通过缺页异常,来拷贝数据。还有进程运行过程中,要动态分配内存,比如malloc时,也只是分配了虚拟内存,即为这块虚拟内存对应的页表项做相应设置,当进程真正访问到此数据时,才引发缺页异常。

    在这里插入图片描述

    在这里插入图片描述
    在进程里平时所说的指针变量,保存的就是虚拟地址。当应用程序使用虚拟地址访问内存时,处理器(CPU)会将其转化成物理地址(MMU)。

    这样做的好处在于:
    进程隔离,更好的保护系统安全运行
    屏蔽物理差异带来的麻烦,方便操作系统和编译器安排进程地址

    推荐一篇好文
    https://www.cnblogs.com/clover-toeic/p/3754433.html

    https://blog.csdn.net/qq_38526635/article/details/81981971?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164552919516780269823158%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=164552919516780269823158&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogsobaiduend~default-1-81981971.nonecase&utm_term=RAM+ROM+cpu&spm=1018.2226.3001.4450

    https://www.jianshu.com/p/b6356e0ec63c

    展开全文
  • 彻底搞懂虚拟内存,虚拟地址,虚拟地址空间

    千次阅读 多人点赞 2021-04-27 18:20:55
    程序经过编译后,变成了...现代操作系统采用的是虚拟地址,这也是本篇文章阐述的重点,但虚拟地址是由1~3阶段发展而来的,所以也有必要阐述1~3三种访问方式。 直接访问 直接访问很好理解,程序经过编译后,生成了可执行

    程序经过编译后,变成了可执行的文件,可执行文件主要包括代码和数据两部分,代码是只读的,数据则是可读可写的。

    可执行文件由操作系统加载到内存中,交由CPU去执行,现在问题来了,CPU怎么去访问代码和数据?,访问的方式经历过四个阶段:

    1.直接访问

    2.段基址+段偏移地址

    3.段选择子+段偏移地址

    4.虚拟地址

    现代操作系统采用的是虚拟地址,这也是本篇文章阐述的重点,但虚拟地址是由1~3阶段发展而来的,所以也有必要阐述1~3三种访问方式。

    直接访问

    直接访问很好理解,程序经过编译后,生成了可执行文件,编译器给每行数据和代码分配了一个唯一的地址,如下图

    彻底搞懂虚拟内存,虚拟地址,虚拟地址空间

    可执行文件

    如上图可执行文件中1000~1024之间的地址,加载到内存后,内存的地址也是1000~1024,在可执行文件中分配的唯一地址就是内存中的物理地址,这就叫直接访问,直接定访问干脆直接,没有那些弯弯绕。

    当时问题也不少,例如同一个可执行文件不能同时执行,它们的物理地址一样,冲突了,必须一个接一个,还有就是可执行文件的物理地址已经固定了,如果想在其它物理地址运行,必须地重新编译,生成新的物理地址。

    可见直接定位是计算机发展早期的产物,早期没有那么多的程序要运行,程序都是一个接一个地去执行的,因此早期这种定位比较简单,直接高效。

    段基址+段偏移地址

    随着多任务需求的来临,现在内存中要并发运行多个程序,虽然采用直接定位把每个不同的程序放在不同的内存段中,勉强可以满足,但是一个可执行文件不能同时运行多个,另外程序必须在固定的物理地址运行,灵活性大大减弱,调度起来也是非常麻烦,因此CPU设计师和操作系统开发人员发明了段基址+段偏移地址。

    Inter 8086处理器就是采用这种定位方式,我们知道可执行文件主要分为数据段和内存段,如下图

    彻底搞懂虚拟内存,虚拟地址,虚拟地址空间

     

    由上图红色部分可知,0,4,8就是相对于数据段的偏移地址,0,4,8,12是相对于代码段的偏移地址。

    在可执行文件中,一个段的偏移地址是固定的,无论可执行文件加载到内存的什么位置,这个偏移地址是固定的。

    当可执行文件加载到内存时,先在内存中分配一个数据段和代码段,这两个段理论上可以不挨着,一般情况下,代码段和数据段是挨着的,代码段和数据段在内存中都有一个起始地址,这个起始地址就叫做段基址,这个段基址是放在段寄存器里,例如代码段基址放在CS寄存器,数据段基址放在DS寄存器,当然还有其他的段例如栈段,栈段刚开始大小为0,随着程序的运行入栈,出栈,这个栈段在不断扩展,当然,咋们主要说的是数据段和代码段,栈段只是简单带过。

    假设可执行文件被加载到了内存中,如下图

    彻底搞懂虚拟内存,虚拟地址,虚拟地址空间

     

    如上图所示,代码段被布局到以0x00600000为起始地址的内存中,数据段被布局到以0x00601000为起始地址的内存中。

    当CPU开始执行代码段的第一条指令时,会将代码段的起始地址放入到段寄存器中,此时CS代码段寄存器中存储的就是0x00600000,然后开始从起始地址处开始执行第一条代码指令,此时把代码指令的偏移地址放入到IP寄存器中,IP寄存器存储的就是0,所以CPU要定位一条代码指令时通过CS:IP的方式定位的,如下图所示

    彻底搞懂虚拟内存,虚拟地址,虚拟地址空间

    定位指令

    当CPU执行到0x00600000处的代码指令时,该指令为MOV AX,[0],该指令的意思是把地址0处的数据存储到AX寄存器,这个0就是数据段的偏移地址,此时CPU会将数据段的起始地址加入到DS段寄存器中,然后将数据段寄存器的值+偏移地址即0x00601000+0=0x00601000定位到了数据123,然后将123存储到AX寄存器中。

    彻底搞懂虚拟内存,虚拟地址,虚拟地址空间

    定位数据

    上述过程就是【段基址+段偏移地址】的定位方式,之所以把起始地址加入到寄存器中,也是为了后续再执行指令或者获取数据时,可以直接从寄存器获取,加快CPU执行的速度。

    段选择子+段偏移地址

    【段选择子+段偏移地址】与【段基址+段偏移地址】有些相似之处,之所以采用【段选择子+段偏移地址】主要是为了安全,原来的【段基址+段偏移地址】方式,程序员可以直接跳转到其他代码段和数据段,没有任何限制,安全性全依赖于程序员的职业操守和水平,因此CPU设计者就发明了【段选择子+段偏移地址】。

    段选择子+段偏移地址】中的段选择子可以认为是一个索引,这个索引指向了全局段描述符表中的一项,全局段描述表存储在内存中,它的起始地址存储在全局段描述符寄存器中。

    全局段描述符表有很多个段描述符,每个段描述占用8个字节,这个段描述符里面就包括了段基址,另外还有一些安全性相关的描述信息例如段的可读,可写,可执行,段的大小等。

    段选择子存储在了段寄存器中,总共16位,其中高13位就是全局段描述表的索引。

    当CPU开始执行代码段的第一条指令时,会将代码段的选择子放入到CS段寄存器中,然后CPU从段寄存器中的获取段选择子,然后截取选择子的高13位获取索引,然后根据全局描述符表寄存器的地址找到全局描述符表的起始地址,根据起始地址+索引*8找到段描述符,然后根据段描述符获取段的基址,段的基址加上ip寄存器中的偏移地址就是指令的物理地址,如下图所示1~6步骤所示

    彻底搞懂虚拟内存,虚拟地址,虚拟地址空间

    定位指令

    当CPU执行到0x00600000处的代码指令时,该指令为MOV AX,[0],该指令的意思是把地址0处的数据存储到AX寄存器,这个0就是数据段的偏移地址,此时CPU会将数据段的选择子加入到DS段寄存器中,然后CPU获取段选择的高13位获取索引,然后根据全局描述符表寄存器的地址找到全局描述符表的起始地址,根据起始地址+索引*8找到段描述符,然后根据段描述符获取段的基址,段的基址加上数据段的偏移地址就是数据的物理地址,如下图1~6步骤所示

    彻底搞懂虚拟内存,虚拟地址,虚拟地址空间

    定位数据

    上述过程就是【段选择子+段偏移地址】的定位方式。

    虚拟地址

    现代的操作系统和CPU未打开分页时采用的是【段选择子+段偏移地址】访问代码和数据,而一旦打开分页时,经过【段选择子+段偏移地址】得到的地址不再是物理地址了,而是叫做虚拟地址,默认则是打开分页的。

    现代的操作系统和CPU采用的平坦模型,平坦模型就是整个内存就一个段,因此段基址就是0,段偏移地址就等于虚拟地址了。

    下面将从以下几个方面来阐述虚拟地址相关的话题。

    1.什么是虚拟地址,物理地址,虚拟地址空间,物理地址空间,虚拟内存,物理内存?

    2.什么是进程虚拟地址空间?

    3.什么是虚拟页,物理页?

    4.什么是页表?

    5.虚拟地址怎么样访问物理内存?

    什么是虚拟地址,物理地址,虚拟地址空间,物理地址空间,虚拟内存,物理内存?

    虚拟地址空间是虚拟地址的集合,假设虚拟地址空间是N位的,它的地址范围为{0~2的N次方-1}即它有2的N次方个虚拟地址,例如16位的虚拟地址空间,它的地址范围为{0~65535},这意味着16位的虚拟地址空间有65536个虚拟地址。

    物理地址空间是物理地址的集合,假设物理地址空间有M个字节,它的地址范围为{0~M-1},M不一定是2的多少次幂,例如M=100,表示物理地址空间大小为100个字节,它的地址范围为{0~99},通常情况下物理地址空间是2的幂次方,例如65536,这也是为了计算机方便处理而已,并不是强制要求的。

    物理内存可以认为是一个的物理字节数组,每个物理地址指向这个物理字节数组中的一项。

    虚拟内存也一样,它也可以认为是一个物理字节数组,不过这个字节数组是存储在磁盘上。

    物理地址空间是物理内存的范围,虚拟地址空间是虚拟内存的范围,物理地址空间中的每个物理地址都是实打实地指向了具体的存储单元,虚拟地址空间中每个虚拟地址指向哪里有3种情况:

    a.未分配,这个虚拟地址仅仅是个数字而已,没有任何指向。

    b.未缓冲,这个虚拟地址指向了磁盘的某个字节存储单元,里面存储了指令或者数据。

    c.已缓冲,这个虚拟地址指向了物理内存的某个字节存储单元,里面存储了指令或者数据。

    2.什么是进程虚拟地址空间?

    操作系统加载可执行文件后,创建了一个进程,这个进程就有了自己的虚拟地址空间,每个进程的虚拟地址空间都一样,如下图所示

    彻底搞懂虚拟内存,虚拟地址,虚拟地址空间

    进程虚拟地址空间

    如上图所示,进程的虚拟地址空间被统一划分成了多个固定区域,例如代码区,数据区,堆区,共享区,栈区,内核区。

    代码区和数据区域:来自于可执行文件,代码区和数据区挨着,代码区总是在0x0040000地址以上,0x0040000地址以下另有它用。

    运行时堆区域:它初始化大小为0,随着动态分配内存(malloc),运行时堆不断往高地址方向扩展,有个指针brk指向了堆的最高地址。

    共享库的内存映射区域:这个区域是一些标准的系统库,这个共享库在物理内存中只存储一份,每个进程将这个区域的虚拟地址映射到同一份共享库物理内存上。

    用户栈区域:这个区域紧挨着内核区域,处于高地址处,随着用户栈的出栈,入栈,动态扩展,入栈向低地址方向扩展,出栈则向高地址方向收缩,栈顶指针存储在栈寄存器(ESP)中。

    内核区域:这个区域是操作系统自己代码,数据,栈空间,内核在物理内存中只存储一份,每个进程将这个区域的虚拟地址映射到同一份内核物理内存上。

    彻底搞懂虚拟内存,虚拟地址,虚拟地址空间

    内核和共享库的映射

    3.什么是虚拟页,物理页?

    现代操作操作和CPU将物理内存按照固定的页大小分成很多份,每一份叫做物理页(PP),每一份有一个编号叫做物理页号(PPN),这个物理页大小通常是4KB,例如一个物理内存大小为20KB,这个物理内存可以分成5个物理页,那么物理页号(PPN)就是0,1,2,3,4。

    虚拟内存也一样,它的页大小与物理内存的页大小相同,虚拟内存也被分成了很多份,每一份叫做虚拟页(VP),每一份的编号叫做虚拟页号(VPN),例如假设虚拟页大小为4KB,一个虚拟内存大小为10KB,这个虚拟内存可以分成2个虚拟页(VP),虚拟页号(VPN)就是0,1

    每个物理页存储在物理内存上,每个虚拟页存储在磁盘上,如下图所示

    彻底搞懂虚拟内存,虚拟地址,虚拟地址空间

    虚拟内存和物理内存

    上图的虚拟内存有8个虚拟页,物理内存有6个物理内存页,虚拟页存储在磁盘上,物理页则存储在DRARM上。

    每个虚拟页可以有三种状态,未分配,已缓冲,未缓冲

    未分配:虚拟页还没有分配磁盘空间

    已缓冲:虚拟页缓冲或者映射在了物理页上。

    未缓冲:虚拟页分配了磁盘空间,但没有在物理页上缓冲。

    通常操作系统加载可执行文件后,创建了一个进程,这个进程就有了虚拟地址空间,这并不意味着可执行文件已经从磁盘加载到内存中了,操作系统只是为了进程虚拟地址空间的每个区域分配了虚拟页。

    代码和数据区域的虚拟页被分配到了可执行文件的适当位置,此时虚拟页状态为未缓冲,虚拟页指向了磁盘地址。

    操作系统和共享库的虚拟页被映射到了物理内存,因为操作系统和共享库已经在物理内存了,这些虚拟页的状态为已缓冲。

    用户栈,运行时堆的虚拟页没有任何分配,不占用任何空间,这些虚拟页的状态为未分配。

    那么进程虚拟地址空间的代码和数据,用户栈,运行时堆的物理内存什么时候分配呢,答案就是处理器用虚拟地址执行代码,读取数据时,这个下一章阐述。

    虚拟地址访问物理内存

    先普及几个概念:

    VPO即虚拟页偏移量:

    虚拟地址由虚拟页号+虚拟页偏移量组成,虚拟页偏移量是相对某个虚拟页的偏移量。

    PPO即物理页偏移量:

    物理地址由物理页号+物理页偏移量组成,物理页偏移量是相对某个物理页的偏移量,

    VPO等于PPO

    页表(Page Table)PT:

    页表是建立虚拟页号和物理页号映射关系的表结构,每个页表项(PTE)包括了有效位,物理页号,磁盘地址等信息,如下图:

    彻底搞懂虚拟内存,虚拟地址,虚拟地址空间

    页表与物理内存,虚拟内存的关系

    由上图可以得知,操作系统可以根据页表项的有效位和地址信息判断出虚拟页目前所处的状态即未分配,已缓冲,未缓冲

    如上图所示,页表有8个页表项,每个页表项里包含一个有效位和地址信息。

    当页表项 PTE n的页表项有效位为0时,表示虚拟页 n没有缓冲在物理内存,可能在磁盘或者未分配,例如PTE 0页表项里存储的是null,表明虚拟页0即VP0是未分配状态,PTE 3里存储的是磁盘地址,表明虚拟页 3即VP3在磁盘里,但没有缓冲,VP3状态为未缓冲。

    当页表项PTE n的页表项的有效位为1时,表示虚拟页n缓冲在物理内存,PTE n存储了物理页号,虚拟页 n的状态为已缓冲,例如PTE 1的页表项,有效位为1,则虚拟页VP1缓冲在了物理页中。

    页表基址寄存器(PTBR):

    每个进程都有自己的页表,CPU执行某个进程时,会先把该进程的一级页表起始地址存储到页表基址寄存器,这样CPU查找一级页表起始地址可以直接从寄存器查找,加快了查找效率。

    好了,概念介绍到这里,先来看看虚拟地址翻译物理地址的过程,按照一级页表来演示,如下图所示:

    彻底搞懂虚拟内存,虚拟地址,虚拟地址空间

    虚拟地址翻译物理地址

    上图为虚拟地址翻译物理地址的示意图,可以看出VPO等于PPO。

    下面看看计算机各个部件是怎么通过虚拟地址访问物理内存的。

    处理器根据虚拟地址访问物理内存的分为页表项命中和页表项未命中两种情况,页表项命中意味着页表项的有效位为1,页表项存储的是物理页号,虚拟页缓冲在物理页中,未命中意味着页表项有效位为0,此时需要发送缺页中断。

    页表项命中的步骤如下图:

    彻底搞懂虚拟内存,虚拟地址,虚拟地址空间

    页表项命中翻译步骤

    1.CPU将虚拟地址(VA)送入MMU,MMU根据页表基址寄存器中页表的起始地址加上虚拟页号,找到了页表项的物理地址PTEA。

    2.MMU将PTEA送入到高速缓冲或者内存。

    3.从高速缓冲或者内存中找到页表项(PTE),返回页表项(PTE)给MMU。

    4.MMU根据PTE找出物理页号,然后加上虚拟页偏移量形成物理地址(PA),送入到高速缓冲或者内存。

    5.高速缓冲或者内存获取数据,返回数据给处理器。

    页表项未命中的步骤如下图:

    彻底搞懂虚拟内存,虚拟地址,虚拟地址空间

    页表项未命中翻译步骤

    1.CPU将虚拟地址(VA)送入MMU,MMU根据页表基址寄存器中页表的起始地址加上虚拟页号,找到了页表项的物理地址PTEA。

    2.MMU将PTEA送入到高速缓冲或者内存。

    3.从高速缓冲或者内存中找到页表项(PTE),返回页表项(PTE)给MMU。

    4.MMU根据PTE,发现页不在内存中,未命中,因此MMU发送一个缺页中断,交由缺页异常处理程序处理。

    5.缺页异常处理程序根据页置换算法,选择出一个牺牲页,如果这个页面已经被修改了,则写出到磁盘上,最后将这个牺牲页的页表项有效位设置为0,存入磁盘地址。

    6.缺页异常程序处理程序调入新的页面,如果该虚拟页尚未分配磁盘空间,则分配磁盘空间,然后磁盘空间的页数据拷贝到空闲的物理页上,并更新PTE的有效位为1,更新物理页号,缺页异常处理程序返回后,再回到发生缺页中断的指令处,重新按照页表项命中的步骤执行。

    虚拟地址翻译物理地址的过程介绍完了,另外要说的是现代的CPU和操作系统为了加快虚拟地址翻译物理地址的过程,做了以下两点优化:

    1.建立了虚拟号(VPN)和页表项(PTE)的映射关系,存储在TLB中,当MMU根据虚拟地址获取页表项时,先查询TLB,在TLB找到了页表项后,就不需要从高速缓冲或者内存中获取了,找不到了才会计算页表项地址PTEA,然后再从高速缓冲或者内存中获取页表项(PTE)。

    2.某些热点物理地址对应的数据,存储在L1缓冲中,MMU根据物理地址获取页表项或者代码数据时,先从L1缓冲中获取,找不到再从内存中获取。

    上述的翻译过程是通过一级页表来翻译,现在操作系统支持多级页表,多级页表与一级页表比较类似,如下图所示:

    彻底搞懂虚拟内存,虚拟地址,虚拟地址空间

    K页表

    上图为K级页表,页表基址寄存器存储的是一级页表的地址,1到K-1的页表的每一项存储的下一级页表的起始地址,K级页表的每一项存储的是物理页号或者磁盘地址。

    好了,关于虚拟地址,虚拟内存,虚拟地址空间的话题就介绍到这里了。

     

    原文链接:https://www.toutiao.com/i6952760982372385317/

    展开全文
  • ⭐️这篇博客就要和大家介绍进程地址空间相关内容,学完这个部分,我们会对进程的地址空间有一个全选的了解 目录程序地址空间进程地址空间 程序地址空间 先看厦门下面一张图,在之前C/C+博客的内存管理中放过这...

    ⭐️这篇博客就要和大家介绍进程地址空间相关内容,学完这个部分,我们会对进程的地址空间有一个全新的了解


    🌏程序地址空间

    先看厦门下面一张图,在之前C/C+博客的内存管理中放过这张图,相信大家对这个不陌生吧。这篇博客有详细的介绍——C/C++内存管理
    在这里插入图片描述
    下面我们通过一个代码来证明上面的地址空间分布图:
    在这里插入图片描述

    代码运行结果如下:
    在这里插入图片描述
    为了更深入地理解,我们用做了这样一个测试:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    int g_val = 10;
    
    int main()
    {
    	int ret = fork();
    	if (ret < 0){
    		// error
    		perror("fork error");
    		return 0;
    	}
    	else if (ret == 0){
    		// child
    		while (1){
    			printf("I am child,pid:%d %d:%p\n", getpid(), g_val, &g_val);
    			sleep(1);
    		}
    	}
    	else{
    		// parent
    		while (1){
    			printf("I am parent:pid:%d %d:%p\n", getpid(), g_val, &g_val);
    			sleep(1);
    		}
    	}
    	return 0;
    }
    

    代码运行结果如下:
    在这里插入图片描述
    可以看出的是,父子进程中对于同一个变量打印的地址是一样的,这是因为子进程以父进程为模板,因为都没有对数据进行修改,所以这里变量地址也是一样的。
    我们再看一段代码,对上面的代码进行一些修改:

    [wxj@VM-0-9-centos test_3_13]$ cat test.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    int g_val = 10;
    
    int main()
    {
    	int ret = fork();
    	if (ret < 0){
    		// error
    		perror("fork error");
    		return 0;
    	}
    	else if (ret == 0){
    		// child
    		int count = 0;
    	    while (1){
    	      printf("修改数据前:\n");
    	      printf("I am child,pid:%d %d:%p\n", getpid(), g_val, &g_val);
    	      sleep(1);
    	      count++;
    	      if (count == 3){
    	        g_val = 20;// 子进程对数据进行修改
    	        printf("修改数据后:\n");
    	    }
    	}
    	else{
    		// parent
    		while (1){
    			printf("I am parent:pid:%d %d:%p\n", getpid(), g_val, &g_val);
    			sleep(1);
    		}
    	}
    	return 0;
    }
    

    代码运行结果如下:
    在这里插入图片描述
    可以发现,子进程对数据进行修改后,父子进程打印g_val的值是不一样的,但是地址却是一样的。
    分析结果得出的原因:

    在fork创建子进程那一篇博客中,我们说过,父子进程共享代码和数据,但是如果有一方对数据做出了修改,那么修改方将堆被修改的这一份数据写实拷贝,这一份数据各自私有,所以根据我们现有的知识可以推断出变量g_val在父子进程中的地址应该是不一样的,但是测试发现他们的这个变量地址是一样的,但是内容却不一样。
    难道对于同一块空间可以有两份不同的数据吗?
    答案是否定的。
    所以我们可以得出结论,我们上面看到的地址绝对不是物理地址

    事实上,在Linux地址下,这种地址叫做虚拟地址(下面讨论)。且平时我们在C/C++语言打印所看到的地址都是虚拟地址,物理地址对于我们用户是看不到的,由OS管理。

    🌏进程地址空间

    我们之前所说的程序地址空间是不准确的,准确的说应该是进程地址空间。进程地址空间也是由OS所创建的一个结构体——mm_struct,其中对虚拟地址每个区域进行了对应的划分(从0x00000000到0xffffffff)它也是被放在PCB中管理起来的,可以通过PCB找到对应的进程地址空间。每个进程都要一个进程地址空间,也就是每个进程都认为自己独享内存资源。所以说进程地址空间其实是一个虚拟地址空间。
    虚拟地址空间 每个进程存放的是一个虚拟地址空间,OS会将虚拟地址通过某种映射关系映射到对应的物理地址空间,从而得到自己的那一份数据。(看图)
    在这里插入图片描述

    父子进程各种有一份虚拟空间地址,在子进程刚被创建时,父子进程代码和数据共享,所以此时虚拟地址空间的内容是基本一样的(当然有部分数据不同,比如各子的id等),且映射关系也是一样的,但是当子进程对数据进行修改时,子进程对那份数据进行写时拷贝,所以物理空间地址发生了变化,但是虚拟地址还是没有发生变化,只是改变了子进程的页表中那份虚拟地址的映射关系而已,所以两个相同的虚拟地址在父子进程分别看到了不同的物理地址空间。

    下面我们再来思考几个问题:

    1. 父子进制之间如何做到具有独立性?

    父子进程的数据和代码都是共享一份的,但是如果有一方试图写入数据,那么写入方将对修改的数据进行写时拷贝,且修改页表中该数据的虚拟地址映射到物理地址的关系。从而父子进程可以看到属于各自的那一份数据,从而达到独立性

    1. 虚拟地址空间带来的好处有哪些?

    a.有了虚拟地址空间,它和物理内存中间添加了一个软件层(页表),这样可以完成有效地对进程内存操作的权限管理,每个进程就要通过虚拟地址和页表中的映射关系来访问各自的物理内存,从而起到保护物理内存的作用。
    b.将内存申请和内存使用的概念在时间上划分清楚,通过虚拟地址空间来屏蔽底层内存申请的过程(进程也不再关心该过程),从而达到进程和OS进行内存管理操作,进行进程调度和内存管理进行解耦
    c.虚拟地址空间可以将空间连续化,降低了异常越界访问的概率。
    d.有了虚拟地址空间,每个进程认为自己独享整个内存资源。每个进程将以相同的方式看待内存,这样就大大地提高了操作系统的工作效率。举例:CPU在对每个进程中的代码执行时,要找到代码的起始地址,且只需要查找固定的虚拟地址,因为对于不同的进程地址空间有不同的映射关系,所以这个固定的虚拟地址在不同的进程中会映射到不同的物理地址中,找到相关代码和数据,所以CPU可以很快地查找到程序运行的起始位置。
    e.站在CPU和应用层角度,看待内存的方式是统一的,且每个空间区域的相对位置是比较确定的。

    1. 伪造的虚拟地址能够进行访问吗?

    答案是不可以的。因为伪造的虚拟地址在页表中可能找不到。页表中其实是有对应的权限管理的,比如代码区是只读的,常量字符串是只读的,某些数据是可读可写的,所以即使找到了,由于该虚拟地址有相应的权限限制,伪造的虚拟地址也不能够进行访问,这样也是保护物理内存的一种机制。

    1. 再次理解进程和进程的创建?

    进程是被加载到内存中的程序,其中包含代码和相关的数据,还有操作系统为之创建的相关的数据结构,其中有PCB(task_struct)、进程地址空间(mm_struct)和页表,我们可以通过PCB找到对应的mm_struct。

    🌐总结

    了解了进程地址空间的内容,我们有重新认识了每个进程中的内存地址分布。操作系统给每个进程都画了一张大饼,让他们感觉自己独享一块空间,这样它就不会关心物理内存使用情况,这样真的是大大提高了操作系统工作效率。是否感觉写操作系统的大佬是有多么的牛逼了吗?个人感觉实在是牛。今天的内容就先到这里了,喜欢的话,欢迎点赞、关注和收藏~
    在这里插入图片描述

    展开全文
  • 内存管理--虚拟地址空间布局架构

    千次阅读 2022-04-23 21:31:40
    内存管理是内核中非常重要的一个子系统,它主要分为三个层面用户空间,内核空间,硬件 1 用户空间: 主要是针对malloc/free,new/delete 2 内核空间: 内核中的sbrk/brk等系统调用 3 硬件:处理器包含一个内存管理...
  • 在早期的计算机中,要运行一个程序,会把这些程序全都装入内存,程序都是直接运行在内存上的,也就是说程序中访问的内存地址都是实际的物理内存地址。当计算机同时运行多个程序时,必须保证这些程序用到的内存总量要...
  • 2.4父子进程虚拟地址空间情况

    千次阅读 2021-02-07 12:53:46
    参考:牛客网 C++高薪求职项目《Linux高并发服务器开发》2.4父子进程虚拟地址空间情况 专属优惠链接: https://www.nowcoder.com/courses/cover/live/504?coupon=AvTPnSG
  • 虚拟地址空间程序实例,包含内核地址空间和用户空间地址空间。虚拟地址空间程序实例,包含内核地址空间和用户空间地址空间。虚拟地址空间程序实例,包含内核地址空间和用户空间地址空间。虚拟地址空间程序实例,包含...
  • 虚拟地址空间对应实际物理地址的理解

    千次阅读 多人点赞 2018-12-18 23:22:16
    在 早期的计算机中,要运行一个程序,会把这些程序全都装入内存,程序都是直接运行在内存上的,也就是说程序中访问的内存地址都是实际的物理内存地址。当计算机同时运行多个程序时,必须保证这些程序用到的内存总量...
  • 进程虚拟地址空间

    千次阅读 2019-07-30 21:31:02
    每个程序在被运行起来之后,它将拥有自己独立的虚拟地址空间这个虚拟地址空间的大小由计算机的硬件平台决定,具体是由cpu的位数决定的。比如32位的平台决定了虚拟地址空间位4G。 这4G空间的分配如下 1 内核空间 最...
  • 虚拟地址空间映射到物理地址空间

    千次阅读 2018-09-07 09:06:45
    虚拟地址空间映射到物理地址空间参考如下  当处理器读或写入内存位置时,它会使用虚拟地址。作为读或写操作的一部分,处理器将虚拟地址转换为物理地址。通过虚拟地址访问内存有以下优势:  程序可以使用一系列...
  • 虚拟内存/虚拟地址空间

    千次阅读 2019-04-29 11:01:02
    虚拟内存: 也被称作“页面文件”,是一种逻辑上扩充物理内存的技术。...虚拟地址空间:32位CPU的地址总线的是32位的,也就是说可以寻找到2^32(4G)的地址空间。我们的程序被CPU执行,就是在0x000...
  • 作者简介:本文由西邮陈莉君教授研一学生贺东升编辑,梁金荣、张孝家校对建议结合之前的《linux的内存寻址方式》看。Linux可执行文件与进程的虚拟地址空间一个可执行文件被执行的同时也伴随...
  • Linux虚拟地址空间

    千次阅读 2018-11-02 21:18:47
    Linux虚拟地址空间 注:本文来自多篇博客整理,具体博客链接在博客下方 在多任务操作系统中,每个进程都运行在属于自己的内存沙盘中。这个沙盘就是虚拟地址空间(Virtual Address Space),在32位模式下它是一个4GB的...
  • 虚拟内存技术使得每一个进程在运行过程中,始终都是显式独自占用当前系统内存资源,事实上,所有进程共享同一个物理内存,每一个进程只把自己目前需要的虚拟...(1)虚拟内存技术扩大了各自进程的地址空间 (2)每个进
  • 1.虚拟内存是内存管理的一种方式, 它在磁盘上划分出一块空间由操作系统管理,当物理内存耗尽是充当物理内存来使用。...2.虚拟地址空间:在多任务操作系统中,每个进程都运行在属于自己的内存沙盘中,这...
  • 虚拟地址空间 及 页表 详解

    千次阅读 2019-05-06 13:24:03
    虚拟地址空间 进程地址空间由进程可寻址的虚拟内存组成,内核允许进程使用这种虚拟内存的地址。每个进程都有一个 32位或64位 的平坦地址空间,空间的大小取决于体系结构。(平坦指的是地址空间范围是一个独立的连续...
  • 进程虚拟地址空间分布

    千次阅读 2018-05-02 21:26:54
    (1)由于系统内核中有些代码、数据是所有进程所公用的,所以所有进程的进程地址空间中有一个专门的区域存放公共的内核代码和数据,该区域内的内容相同,且该虚拟内存映射到同一个物理内存区域。  (2)进程在执行...
  • 物理地址空间和虚拟地址空间

    千次阅读 2018-02-18 21:43:18
    因为CPU是32位的,其地址总线是32位的,所以其地址总线可编码的个数是2^32(4G),这2^32个物理地址的集合就是物理地址空间。这与38译码器的道理是一样的,这一点很好理解。 二、虚拟地址(空间) &nbsp; &...
  • 虚拟地址空间 Virtual Address Space

    千次阅读 2019-06-20 07:23:54
    读《Computer System》,遇到 Virtual Address Space 这个概念,即虚拟地址空间,以前也看过,不过都是知其然不知其所以然,故本文做一下详细的概念解析:起源和发展和现状,起到什么作用,为何要创造这个概念。...
  • 根据crash学习ARM64虚拟地址空间布局

    千次阅读 2020-04-08 11:02:49
    每个进程感觉自己都能看到整个虚拟地址空间,大小用TASK_SIZE表示 32位用户空间程序:TASK_SIZE=TASK_SIZE_32=0x100000000=4G (64位内核运行32位应用程序时) 64位用户空间程序:TASK_SIZE=TASK_SIZE_64=1如果地址...
  • 》,我们知道了CPU是如何访问内存的,本篇文章我们来讲下虚拟地址空间和物理地址空间的映射。 通常32位Linux内核地址空间划分0~3G为用户空间,3~4G为内核空间。注意这里是32位内核地址空间划分,64位内核地址空间...
  • Windows虚拟地址空间

    千次阅读 2017-04-02 12:28:28
    整理关于WIndows虚拟地址空间的一些知识。如果有什么不对,希望有人可以提出...
  • 程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓冲区。 2.方便进程之间隔离 不同进程使用的虚拟地址彼此隔离。一个进程中的代码无法更改正在由另一进程使用的物理内存。 3.方便OS使用你那...
  • Linux虚拟地址空间分布

    千次阅读 2017-06-16 23:35:40
    Linux虚拟地址空间分布

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 353,258
精华内容 141,303
关键字:

虚拟地址空间