物理地址,虚拟地址和逻辑地址

关于物理地址,虚拟地址和逻辑地址的定义和之间的关系

虽然写了这么多知识点但脑袋还是乱乱的,希望哪天突然开窍叭🥱

理解内存

我们平时使用的各种数据都是存储在硬盘等存储器上,但硬盘的运行速度很慢。所以需要运行程序或者使用数据时,这些数据必须从硬盘上转到另一种容量小但速度快很多的存储器,之后才送进CPU进行处理。这中间的存储器就是内存。

无论何种存储器,软盘、硬盘、光盘或者内存,都有地址。

其中:

  • 我们程序所使用的内存地址叫做虚拟内存地址Virtual Memory Address
  • 实际存在硬件里面的空间地址叫物理内存地址Physical Memory Address)。

物理地址

物理地址就是常说的内存地址,是内存当中存储数据的一个标识,并不是数据本身,通过内存地址可以找到内存当中存储的数据。

计算机将内存划分为一个个小的内存单元,同时对其编号,这样就能有效管理内存。在空间划分实践中,一个内存单元的大小为1字节。一个字节是八个比特,相当于八个二进制位,两个十六进制位。

可以理解为一栋叫内存的楼,每一套商品房都有自己的门牌号叫内存单元,每一个家庭是一个数据,每一位家庭成员所居住的小房间则不会进行编号。

每个内存单元都有编号(内存编号),内存编号可以称为地址,在C语言中也称为指针

🌰

在内存中存储”修饰符“或”MOD“,可以示意为:

![]/img/物理地址,虚拟地址和逻辑地址/Snipaste_2023-03-28_20-23-49.png)

数字后面加H表示十六进制

在第一行中,每一格表示一段内存,而格子里的内容是这段内容记下的数据;第二行中每一格内数字就是对应的内存的地址。

汉字在一个地址空间里储存不下,会放进两个连续的地址空间里。而字母或者阿拉伯数字就可以放进一个内存地址里。在上图中,”修“的内存地址为1000H,或者M的内存地址为”1000H”。

虚拟地址

虚拟存储器不是任何实际的物理存储器,而是借助磁盘等辅助存储器来扩大主存容量,使之为更大或更多的程序所使用。

虚拟地址用于指示虚拟存储器的地址,它是用逻辑地址指示的

在程序运行时,我们需要将进程与物理地址映射起来,才能区分这些内存中存储的数据属于哪个进程。但内存大小有限,进程却可以很多,甚至可能同时进行多个进程。为了实现让很多进程共用一个存储资源有限的内存,我们引出虚拟地址的概念,我们先将进程与虚拟地址映射起来,再将虚拟地址与物理地址映射起来。

操作系统引入了虚拟内存,进程持有的虚拟地址会通过 CPU 芯片中的内存管理单元(MMU)的映射关系,来转换变成物理地址,然后再通过物理地址访问内存,如下图所示:

逻辑地址

逻辑地址,就是指机器语言指令中用来指定一个操作数或一条指令的地址,由一个段(segment)和偏移量(offset)组成,说地直白点就是CPU拿到的地址。

段号:用来查找段的起始地址,它被存储在段寄存器当中

偏移地址:是存储单元的物理地址与所在起始段的差值

各种内存的关系

虚拟地址和物理地址

内存分段

程序是由若干个逻辑分段组成的,如可由代码分段、数据分段、栈段、堆段组成。不同的段是有不同的属性的,所以就用分段(Segmentation)的形式把这些段分离出来。

分段机制下的虚拟地址由两部分组成,段选择子段内偏移量

段选择子

段选择子保存在寄存器里。段选择子最重要的是段号,用作段表的索引。段表里保存的是这个段的基地址、段的界限和特权等级等。

段选择子是十六位的,其中十四位表示地址信息

段偏移量

偏移量定义为:把存储单元的实际地址与其所在段的段地址之间的距离称为段内偏移,也称为“有效地址”或“偏移量”。

虚拟地址中的段偏移量应该位于0和段界限之间。如果段内偏移量是合法的,就将其段及地址加到段内偏移量得到物理内存地址。

分段机制会把程序的虚拟地址分成 4 个段,每个段在段表中有一个项,在这一项找到段的基地址,再加上偏移量,于是就能找到物理内存中的地址。

存在的问题

  • 内存碎片

举例来说。假设我们现在占有 1G 的物理内存,用户执行了多个程序,游戏占用了 512MB 内存,浏览器占用了 128MB 内存,音乐占用了 256 MB 内存。

这个时候,我们关闭浏览器,则空闲内存还有 1024 - 512 - 256 = 256MB。但如果这个 256MB 不是连续的,被分成了两段 128 MB 内存,这就会导致没有空间再打开一个 200MB 的程序。

这里的内存碎片的问题共有两处地方:

外部内存碎片,也就是产生了多个不连续的小物理内存,导致新的程序无法被装载;

内部内存碎片,程序所有的内存都被装载到了物理内存,但是这个程序有部分的内存可能并不是很常使用,这也会导致内存的浪费

  • 内存交换效率低

对于外部内存碎片的问题,我们采用内存交换的解决措施。

可以把音乐程序占用的那 256MB 内存写到硬盘上,然后再从硬盘上读回来到内存里。不过再读回的时候,我们不能装载回原来的位置,而是紧紧跟着那已经被占用了的 512MB 内存后面。这样就能空缺出连续的 256MB 空间,于是新的 200MB 程序就可以装载进来。

对于多进程的系统来说,用分段的方式,内存碎片是很容易产生的,产生了内存碎片,那不得不重新重新规划内存区域,这个过程会产生性能瓶颈。

因为硬盘的访问速度要比内存慢太多了,每一次内存交换,我们都需要把一大段连续的内存数据写到硬盘上。

所以,如果内存交换的时候,交换的是一个占内存空间很大的程序,这样整个机器都会显得卡顿。

内存分页

为了解决内存分段的内存碎片和内存交换效率低的问题,就出现了内存分页。分页是把整个虚拟和物理内存空间切成一段段固定尺寸的大小。这样一个连续并且尺寸固定的内存空间,我们叫Page

虚拟地址与物理地址之间通过页表来映射:

页存储在内存里,由CPU的内存管理单元即MMU负责映射转换的工作吗,这样CPU 就可以直接通过 MMU,找出要实际要访问的物理内存地址。

由于内存空间都是预先划分好的,也就不会像分段会产生间隙非常小的内存。采用了分页,那么释放的内存都是以页为单位释放的,也就不会产生无法给进程使用的小内存。

如果内存空间不够,操作系统会把其他正在运行的进程中的「最近没被使用」的内存页面给释放掉,也就是暂时写在硬盘上,称为换出Swap Out)。一旦需要的时候,再加载进来,称为换入Swap In)。所以,一次性写入磁盘的也只有少数的一个页或者几个页,不会花太多时间,内存交换的效率就相对比较高。

在分页机制下,虚拟地址分为两部分,页号页内偏移。页号作为页表的索引,页表包含物理页每页所在物理内存的基地址,这个基地址与页内偏移的组合就形成了物理内存地址

其他东西

再来,为了解决简单分页产生的页表过大的问题,就有了多级页表,它解决了空间上的问题,但这就会导致 CPU 在寻址的过程中,需要有很多层表参与,加大了时间上的开销。于是根据程序的局部性原理,在 CPU 芯片中加入了 TLB,负责缓存最近常被访问的页表项,大大提高了地址的转换速度。


冲浪冲到的,我觉得比起枯燥的文字,这个更好理解


之前觉得自己很牛直接开始做题,靠一些老本还是能做几道题,但最近发现偏移量和内存地址啥的根本不会看也不会算,还是老老实实打基础叭😳


物理地址,虚拟地址和逻辑地址
https://shmodifier.github.io/2023/04/03/物理地址,虚拟地址和逻辑地址/
作者
Modifier
发布于
2023年4月3日
许可协议