虚幻与真实:程序中的地址如何转换?

1)读取指令、读写数据的时候需要和内存进行怎样的交互?

  • 告诉内存芯片:hi,内存老哥请你把 0x10000 地址处的数据交给我……hi,内存老哥,我已经计算完成,请让我把结果写回 0x200000 地址的空间。这些地址存在于代码指令字段后的常数,或者存在于某个寄存器中。

设想一下,如果一台计算机的内存中只运行一个程序 A,这种方式正好用前面 CPU 的实模式来运行,因为程序 A 的地址在链接时就可以确定,例如从内存地址 0x8000 开始,每次运行程序 A 都装入内存 0x8000 地址处开始运行,没有其它程序干扰。现在改变一下,内存中又放一道程序 B,程序 A 和程序 B 各自运行一秒钟,如此循环,直到其中之一结束。这个新场景下就会产生一些问题,当然这里我们只关心内存相关的这几个核心问题。

2) 谁来保证程序 A 跟程序 B 没有内存地址的冲突?

  • 换句话说,就是程序 A、B 各自放在什么内存地址,这个问题是由 A、B 程序协商,还是由操作系统决定?

3)如何解决内存容量问题?

  • 程序 A 和程序 B,在不断开发迭代中程序代码占用的空间会越来越大,导致内存装不下?

4)如果还要拓展其他程序又该怎么办?

  • 如果不只程序 A、B,还可能有程序 C、D、E、F、G……它们分别由不同的公司开发,而每台计算机的内存容量不同。这时候,又对我们的内存方案有怎样的影响呢?

5)怎样完美的解决以上最核心的 4 个问题?

  • 所有的程序都各自享有一个从 0 开始到最大地址的空间,这个地址空间是独立的,是该程序私有的,其它程序既看不到,也不能访问该地址空间,这个地址空间和其它程序无关,和具体的计算机也无关。---虚拟地址

6)什么是虚拟地址?

  • 不存在的地址,逻辑上的,和具体的环境无关。这个环境包括软件环境和硬件环境。

7)我们用 objdump 工具反汇编一下 Hello World 二进制文件看看长什么样?

 
 00000000000004e8 <_init>:
  4e8: 48 83 ec 08           sub   $0x8,%rsp
  4ec: 48 8b 05 f5 0a 20 00   mov   0x200af5(%rip),%rax       # 200fe8 <__gmon_start__>
  4f3: 48 85 c0               test   %rax,%rax
  4f6: 74 02                 je     4fa <_init+0x12>
  4f8: ff d0                 callq *%rax
  4fa: 48 83 c4 08           add   $0x8,%rsp
  4fe: c3                     retq
  • 左边第一列数据就是虚拟地址,第三列中是程序指令,如:“mov 0x200af5(%rip),%rax,je 4fa,callq *%rax”指令中的数据都是虚拟地址。

8)为什么所有的应用程序开始的部分都是这样的?

  • 因为每个应用程序的虚拟地址空间都是相同且独立的。

9)那么这个地址是由谁产生的呢?

  • 链接器

10)什么是物理地址呢?

  • 一个会被地址译码器等电子部件变成电子信号的数据,这个电子信号放到地址总线上去,多个信号组合起来呢就可以表示为我们内存的一个存储单元了。

11)地址总线上的信号(即物理地址)除了可以选择内存之外,还可以选择那些地方?

  • 显卡中的显存、I/O 设备中的寄存器、网卡上的网络帧缓存器。

12)虚拟地址到物理地址如何转换?

  • 转换机构:相当于一个函数:p=f(v),输入虚拟地址 v,输出物理地址 p。

13)那么要怎么实现这个函数呢?

  • 软件方式实现太低效,用硬件实现没有灵活性。

  • 最终就用了软硬件结合的方式实现,它就是 MMU(内存管理单元)。

14)MMU 工作原理框架图是怎样的?

 

 

  • 注意地址关系转换表本身则是放物理内存中的。

15)地址关系转换表中逻辑地址和物理地址的对应关系是怎样的?

  • 把虚拟地址空间和物理地址空间都分成同等大小的块,也称为页,按照虚拟页和物理页进行转换。根据软件配置不同,这个页的大小可以设置为 4KB、2MB、4MB、1GB,这样就进入了现代内存管理模式——分页模型。

16)分页模型框架是怎样的?

 

 

  • 一个虚拟页可以对应到一个物理页,页大小固定。所以在地址关系转换表中,只要存放虚拟页地址对应的物理页地址就行了。

17)MMU是做什么工作的?

  • 接受虚拟地址和地址关系转换表,以及输出物理地址。

18)MMU的工作需要什么前提条件?

  • 必须先开启保护模式或者长模式,实模式下是不能开启 MMU 的。

19)保护模式的内存模型是分段模型,而MMU是分页模型的,那怎么说保护模式下可以工作?

  • 别忘了我们的保护模式下还有个平坦模式,绕过我们的分段模型。

20)地址产生的过程是怎样的?

 

 

21)地址关系转换表更专业的名字是什么?

  • 页表

22)为了提高查询效率,页表是分级的,那么页表分级的结构是怎样的?

  • 一个顶级页目录,多个中级页目录,最后才是页表。

 

 

23)开启 MMU,步骤是怎样的?

  • 使 CPU 进入保护模式或者长模式。

  • 准备好页表数据,这包含顶级页目录,中间层页目录,页表,假定我们已经编写了代码,在物理内存中生成了这些数据。

  • 把顶级页目录的物理内存地址赋值给 CR3 寄存器。

     
     mov eax, PAGE_TLB_BADR ;页表物理地址
     mov cr3, eax
  • 设置 CPU 的 CR0 的 PE 位为 1,这样就开启了 MMU。

     
     ;开启 保护模式和分页模式
     mov eax, cr0
     bts eax, 0   ;CR0.PE =1
     bts eax, 31   ;CR0.P = 1
     mov cr0, eax

     

24)MMU 的主要功能是根据页表数据把虚拟地址转换成物理地址,但有没有可能转换失败?

  • 绝对有可能,例如,页表项中的数据为空,用户程序访问了超级管理者的页面,向只读页面中写入数据。这些都会导致 MMU 地址转换失败。

25)MMU 地址转换失败了怎么办呢?

  • MMU 停止转换地址。

  • MMU 把转换失败的虚拟地址写入 CPU 的 CR2 寄存器。

  • MMU 触发 CPU 的 14 号中断,使 CPU 停止执行当前指令。

  • CPU 开始执行 14 号中断的处理代码,代码会检查原因,处理好页表数据返回。

  • CPU 中断返回继续执行 MMU 地址转换失败时的指令。

26)在分页模式下,操作系统是如何对应用程序的地址空间进行隔离的?

  • 对于每个进程而言,它会误认为(被操作系统欺骗)自己独有所有地址空间,因此它访问地址是不会考虑任何问题的,可是这个地址是虚拟地址,待被MMU翻译后会得到对应的页表,而这个页表由操作系统管理,不同的进程拥有不同的页表,也因此产生了进程地址空间隔离,但是多个进程也是可以共享某个页表,这也是进程通信(IPC)的根本手段。

posted on 2022-03-15 17:27  Love&Share  阅读(424)  评论(0编辑  收藏  举报

导航