寄存器地址和内存地址_每周汇编一点点-助力国产操作系统诞生-存储器及其地址...

说明

上篇介绍了X86架构CPU的64-bit应用程序使用的寄存器。想要CPU运行程序除了内部寄存器外还需要一些外部设备。外部设备中最重要的是存储器,计算机可以没有显示器,可以没有键盘鼠标。但只要有存储器,并且存储器中存储着程序和数据,计算机就可以工作。

本文中将存储器分为两类。以下分类名称非标准名称只是小编自己的叫法,大家领会精神就好。

一类存储器是指CPU可以直接访问的存储器,最为我们熟知的就是内存。

eb982a71625e34f23da67a824ea90fa4.png

二类存储器是指CPU不可直接访问的存储器,为我们熟知的有硬盘、U盘等。

bb482f7b7a768bd5068a18510195fee5.png
ca76aada63cf04083e3f980aa770553c.png

这里说一句目前好多人将手机的存储空间称为手机内存。同款手机会出很多款不同存储空间的版本比如64G版、128G版、256G版。这里的手机存储空间其实类似电脑的硬盘,而不是电脑内存。

CPU可以直接访问一类存储器,执行上面的指令,读写上面的数据。CPU使用地址访问一类存储器。一类存储器的地址类似与火车上的座位号,一个座位号对应一个座位。实质上地址是一个数字每个数字对应一个存储器中的字节(8位二进制数)。地址从0开始,地址0代表存储器中的第一个字节,地址1代表存储的第二个字节,以此类推。

但是由于计算机在实际运行时一共涉及到三个角色,而且三个角色无法使用统一的地址,因此存储器的地址要比座位号复杂一些。这三个角色分别是应用程序操作系统CPU。下面分别讨论着三个角色对地址的使用。

一切都基于以下三个前提

  • 只有真正存储到一类存储器上的程序才能被CPU执行,并且CPU只能通过一类存储器的地址访问其中的指令和数据。
  • 应用程序本身存储在二类存储器上。它们是被操作系统将加载到一类存储器当中,并且通知CPU开始执行的。
  • 程序被加载到一类存储器之前不知道它在存储器上的地址是什么。

应用程序认为的地址-有效地址(effective address)

想要了解有效地址首先要明白什么是程序。程序是有序的计算机指令和数据的集合。程序通常以文件的形式存放在二类存储器上。以下是一个简单程序文件的内容,该程序有3条数据和1条指令。其中指令的作用是将data1和data2相加,并将结果保存到data3中(实际上并没有这样的指令)。

例1

data1data2data3command data3 = data1+data2

因为指令使用了data1、data2、data3因此必须告诉指令三条数据的地址。由前面介绍的前提可知,程序员在编写程序时并不知道三条数据在存储器中的地址。但又必须告诉指令三条数据的地址。此时只有一个办法就是告诉指令,三条数据在文件中的地址。数据和指令在文件中的地址被称为有效地址。

有效地址是怎么编排的呢。简单的方式是将文件的开头认为是地址0,然后每个字节地址加1。比如例中的每条数据占一个字节、指令占两个字节。那么整个文件就占用5个字节。data1位于地址0,data2位于地址1,data3位于地址2,指令位于地址3

c248c7592aa9f1df6fb8b52375a027a9.png

当然也可以有别的方式,比如在文件中加入标签

例2

.DATAdata1data2data3.CODEcommand data3 = data1+data2

加入两个标签后三条数据的有效地址都是相对于.DATA标签来计算的。指令的有效地址是相对于.CODE标签计算的。这样data1和command的有效地址就都是0了。

操作系统认为的地址-虚拟地址(virtual address)

由前提可知操作系统需要将程序加载到一类存储器中才能让CPU执行。那么如何加载呢。

最简单的方法就是将程序加载到于其有效地址相同的存储器空间中。如上节的例1,操作系统将程序加载到一类存储器地址为0的空间中,CPU就可以执行了。但任然存在问题,例2的程序怎么办,data1和command的有效地址都是0,总不能都加载到一类存储器的地址0吧!

当然你可以说我不使用例2的形式,只使用例1的形式。但是同样会出现问题,如果QQ和360都是以例1的形式编写出来的,明显它俩不可能同时被执行,难道这就是3Q大战的起因?显然不是。

解决这个问题的方法是使用一种叫基地址映射的机制。首先操作系统把一个程序加载到内存的某个空闲的空间。然后记录这个空间的基地址(也就是起始地址)。最后在执行指令的时候将程序中的有效地址加上这个基地址就可以正常执行了。而且只要按照CPU的设计,将基地址保存到指定的地方,这个转换是由CPU自动完成的。

6942321f8c3235cc54cbdb69dd241e89.png

上图示意例1和例2同时被加载到内存的场景。例1的基地址是31,因此command加载后的地址就是31+3=34。例2.CODE的基地址是42,因此command加载后的地址是42+0=42。

这样视乎很圆满,但是仍然存在问题。CPU为64位操作系统提供的地址空间是从0到2的64次方。而平时我们的电脑不会装这么大的内存条,小编的机器是32G,我感觉至少超越70%的个人电脑了,但是这个数字远远小于2的64次方。不单是因为装机问题,即使你有钱装那么大的内存条,但理论上X86架构的CPU支持的最大物理内存是2的54次方小于2的64次方。

因此显然通过基地址映射机制获得的内存地址不是真是的物理地址,只是CPU让操作系统认为它可以使用这么大的空间,因此操作系统认为自己可以使用的空间只是一个虚拟空间,对应的虚拟空间中的地址被称为虚拟地址。

CPU认为的地址-物理地址(physical address)

终于说到幕后黑手了。与CPU实际打交道的一类存储器设备,主要是内存,除了内存之外还有其他设备。CPU只能访问真实存在与这些设备上的信息,这些物理设备的空间地址被称为物理地址。

问题又来了,CPU是如何将提供给操作系统的虚拟地址映射成物理地址呢。X86架构的CPU采用了一种称为分页机制的映射方式。这种方式是将虚拟内存映射到大小固定的上。虚拟地址通过一个多级映射表被转换成某个页上的页内地址(如第n页的地址256)。而这个多级映射表是由操作系统来填写的(CPU和操作系统必须紧密配合才能正常工作)。在64-bit模式下,操作系统需要填写一个有4个级别的映射表。

页可以被加载到内存中,也可以保存在二类存储器上。因此如果一个操作系统及其运行的程序将整个虚拟地址空间占满,而这个系统只有32G内存的话,虚拟地址空间中只有32G的内容在物理内存上,其他的内容都被保存在硬盘或其他二类存储器上。

19eba33465962a9303842c7cfdedf861.png

如果要访问那些没有加载到物理内存中的页,首先需要将页加载到内存中,如果内存已满就需要将内存中使用几率最低的页存储到二类存储器上,然后再将要访问的页加载到内存。

总结

最后用一张图总结本文内容。

deb2b98e405827b6413d8671f2d1225d.png

希望大家了解x86汇编,祝独立的可用的国产桌面/服务器操作系统内核早日诞生!~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值