二、Lab1

一、Part 1:PC Bootstrap


时间:2022年1月15日
要进入gdb调试,出现和实验指导中的文件图片,需要分别在两个terminal中运行 make qemu-gdbmake gdb 指令。这里前面一个指令与实验指导不一样,我也没弄懂咋回事,先往后面做吧。


时间:2022年1月16日 一些基础知识补充
文章参考:

1、如何从内存读取指令和数据?

x86 使用“段基址 + 偏移量”的方式来读写内存

2、“段基址” + “偏移量” 寻址方式的由来

了解了 x86 的内存寻址方式,不禁要问:“为什么要这么设计?”这得从英特尔的 8086 CPU 开始讲起。我们有时说起计算机硬件配置的时候经常会说:“我的电脑是 32 位的”。这里的 32 位起始指的是 CPU 内部的“数据总线”宽度,也叫 ALU 的宽度。说白了就是 CPU 一次性传递数据的宽度。

英特尔的 8086 CPU 是 16 位的,如果直接用来表示内存地址的话,16 位最大可以表示的内存地址是 216 = 65536 个地址,每个地址代表一字节的内存数据的话,16 位最多只能支持 64KB 内存,这显然是不够用的。于是英特尔在保持数据线宽为 16 位的同时将地址线的宽度增大到 20 位,也就是说内存地址是 20 位的,这样就可以拥有 220 = 1048576 个地址,最多支持 1MB 的内存,当时的人们认为这样就足够了。

现在问题来了,16 位的数据线宽(寄存器自然也是 16 位的)如何能表示 20 位的地址呢?答案是用两个 16 位的寄存器来表示。这就是“段基址” + “偏移量”寻址方式的由来。一个 16 位的寄存器来表示“段基址”(CS、DS、SS、ES四个寄存器),具体的做法是先将 16 位的“段基址”左移 4 位,然后加上 16 位的“偏移量”最终得到 20 位的内存地址送入地址线。

3、地址卷绕

用两个 16 位的寄存器左移相加来得到 20 位的内存地址这里还是有问题。那就是两个 16 位数相加所得的最大结果是超过 20 位的。例如段基址 0xffff 左移变成 0xffff0 和偏移量 0xffff 相加得到 0x10ffef 这个内存地址是“溢出”的,怎么办?这里 CPU 被设计出来一个“卷绕”机制,当内存地址超过 20 位则绕回来。举个例子你拿 0x100001 来寻址,我就拿你当作 0x00001 。你超出终点我就把你绕回起点。

4、向下兼容的现代 x86 计算机

8086 的年代已经远去。现在的 x86 已早经是 32 位的了(目前 32 位基本已经没有了,64 位是主流了)。但无论位数如何增加,寻址能力如何增大,x86 一直保持着向下兼容的良好传统。即便是当初为 8086 这种 16 位机器写的软件或操作系统(如 DOS)仍能够在现在的 x86 计算机上顺利运行。

那么这种良好的向下兼容性是如何实现的呢?答案是:“开关”。现代的 x86 计算机,无论你是 32 位的还是 64 位的,在开机的那一刻 CPU 都是以模拟 16 位模式运行的,地址卷绕机制也是有效的,所以无论你的电脑内存有多大,开机的时候 CPU 的寻址能力只有 1MB,就好像回到 8086 时代一样。

那么什么时候才结束 CPU 的 16 位模式运行呢?这由你(操作系统)说了算,现代的计算机都有个“开关”叫 A20 gate,开机的时候 A20 gate 是关闭的,CPU 以 16 位模式运行,当 A20 gate 打开的时候“卷绕”机制失效,内存寻址突破 1MB 限制,我们就可以切换到正常的模式下运行了。

5、实模式与保护模式

在“实模式”下没有“虚拟地址”到“物理地址”的转换,“虚拟地址”就相当于是“物理地址”,而想要这些特性就需要对应的把计算机的运行环境切换到“保护模式”下。

就像之前我们讲到的 A20 gate 从 1MB 的内存寻址模式切换到更大的寻址能力一样。x86 架构的计算机为了向下兼容,开机的时候不仅运行在 1MB 内存寻址环境下,这时候也是运行在“实模式”环境下的。同样有一个开关控制着从“实模式”到“保护模式”的切换,这个开关叫“控制寄存器”。

6、保护模式下的分段与分页

前面说道“保护模式”是由硬件和操作系统配合来提供的。“保护模式”涉及的知识非常多,不仅仅只有对内存的管理,还有诸如进程管理、硬件管理等诸多方面,这里只简单介绍一下“保护模式”下的内存管理。“保护模式”实现的两种内存管理方式:“分段式和分页式”。分页式是目前主流操作系统(Windows、Linux、FreeBSD等)所采取的内存管理方式。

“分页式”技术的出现要比“分段式”晚一些,碰上 x86 这样历史悠久的硬件架构就不得不再提“向下兼容”了。所以 x86 的分页式的实现是继续分段式基础上的。所以想要在 x86 上建立起分页式的内存管理就先要建立分段式内存管理,分页式我们暂且不说,先说说分段式。

分段式简单来说就是将内存规划出不同的“片段”来分配给不同的程序(也包含操作系统自己)使用。分页式则是将内存规划成大小相同的“页”,再将这些页分配给各个程序使用。

这里有两个“段”字非常让人容易迷糊。分段式里的段与之前讲过的“段基址”完全是两码事儿。实模式下的段寄存器里的“段基址”实际上还可以算作内存物理地址,它指向的是内存中的一个位置,而在分段式的保护模式下段寄存器里的“段基址”的意义已经发生里改变,它不再是内存的物理地址,而是指向一个内存分段的段索引。在分段模式下,内存被划分为很多个“片段”,程序数据以及指令就放在这些片段中,当要读取内存中具体的数据时,首先要直到这个数据在哪个“片段”里,这时段寄存器里的“段基址”指向某一个内存片段的下标,而这时的“偏移量”则相应的表示为具体的数据在它所在的内存“片段”里的偏移量。

所以在分段模式下,内存里会有一个“表”,这个“表”里存放了每个内存“片段”的信息(如这个“片段”在内存中的地址,这个“片段”多长等),比如我们现在将内存分成 10 个片段,则这时我们有一个“表”,这个“表”有 10 项分别存放着对应这 10 个内存片段的描述信息。这时我有个数据存放在第 5 个片段中,在第 5 个片段的第 6 个位置上,所以当我们想要读取这个数据的时候,我们的数据段寄存器里存放的“段基址”是 5 这个数,代表要去第 5 个片段上找,对应的这时候的“偏移量”就是 6 这样我们就可以顺利的找到我们想要的数据里。

而要想实现在分段式保护模式下成功的寻址,操作系统需要做的就是在内存中建立这个“表”,“表”里放好内存分段的描述信息,然后把这个“表”在内存的什么位置,以及这个“表”里有多少个内存分段的描述信息告诉 CPU。这个“表”有个学名叫 GDT 全局描述符表,这个我们后面还会有介绍。

这里的GDT就是在《操作系统》一门课中所学的段表,段基址就是段表始址。

7、物理地址、线性地址、逻辑地址(虚拟地址)、虚拟内存
  • 物理地址
    这个没什么可说的,非常好理解,物理地址就是内存从硬件角度上真正的地址。所有对内存的寻址最终都要转换到物理地址上才能被识别。

  • 逻辑地址(虚拟地址)
    这两种叫法说的是一种东西。就是我们上面讲的程序基于统一的“假设”通过 N + X 计算出的内存地址。

  • 线性地址
    线性地址的概念是保护模式下才有的,在实模式下逻辑地址就是物理地址,在保护模式下还要根据分段和分页分开说。在分段模式下逻辑地址通过 GDT 转换成线性地址,此时如果没有分页机制那么线性地址就是物理地址,如果有分页机制,那么线性地址要通过 MMU(Memory Manager Unit,内存管理单元) 再一次转换之后才能变成物理地址。

  • 虚拟内存
    我们以 32 位计算机为例,在 32 位计算机上支持的最大内存寻址是 4GB,但是每个计算机上真正有多少内存却是不一定的。同样的 32 位计算机,有的可能只有 1GB 内存,有的只有 2GB 内存,而对于程序来说不应该受到这种硬件配置的影响,无论有多少内存,程序都应该正常运行。这就提出里虚拟内存的概念,就像我们之前说的程序的统一假设一样,对于每个程序来说,我们都统一假设只要你的寻址位宽是 32 位,那我就假设我有 4GB 内存可以利用。而具体有多少内存,如何和逻辑地址对应,这是操作系统需要考虑的事情了。

  • 大小端模式
    大端模式 : 地址的增长顺序与值的增长顺序相同
    小端模式 : 地址的增长顺序与值的增长顺序相反

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值