操作系统-Operating-System第三章02:地址空间和地址生成

B站资源:操作系统_清华大学(向勇、陈渝)

Github资源:chyyuu/os_course_info

参考书籍:Operating systems: internals and design principles

Operating System Concepts

MIT公开课:6.828: Operating System Engineering

其他博客内容[知识点]物理地址(空间)与虚拟地址(空间)1.1 虚拟地址和物理地址操作系统学习笔记(六):地址空间和地址生成

地址空间

以数组为例,在数组中每个元素占一个字节,则可以将这个数组的长度类比于地址空间。

物理地址空间

硬件支持的地址空间

因为CPU是32位的,其总线地址是32位的,所以其地址总线可编码的个数是 2 32 2^{32} 232(4G),这 2 32 2^{32} 232个物理地址的集合就是物理地址空间。

起始地址为0,直到 M A X s y s MAX_{sys} MAXsys

逻辑地址空间

在CPU运行的进程中看到的地址

起始地址为0,直到 M A X p r o g MAX_{prog} MAXprog

操作系统-Operating-System第三章02:地址空间和地址生成

在早期的计算机中,程序是直接运行到物理内存(可以理解为内存条上的内存)上的。也就是说,程序运行的时候直接访问的就是物理地址。如果,我们的一个计算机只运行一个程序,那么只有这个程序所需要的内存空间不超过物理内存空间的大小,就不会有问题。但是,我们正在希望的是在某个时候同时运行多个程序。那么这个时候,就会有个一个问题,计算机如何把有限的物理内存分配给多个程序使用呢?

某台计算机总的内存大小是128M,现在同时运行两个程序A和B,A需占用内存10M,B需占用内存110。计算机在给程序分配内存时会采取这样的方法:先将内存中的前10M分配给程序A,接着再从内存中剩余的118M中划分出110M分配给程序B。这种分配方法可以保证程序A和程序B都能运行,但是这种简单的内存分配策略问题很多。

问题1:进程地址空间不隔离。由于程序都是直接访问物理内存,所以恶意程序可以随意修改别的进程的内存数据,以达到破坏的目的。有些非恶意的,但是有bug的程序也可能不小心修改了其它程序的内存数据,就会导致其它程序的运行出现异常。这种情况对用户来说是无法容忍的,因为用户希望使用计算机的时候,其中一个任务失败了,至少不能影响其它的任务。

问题2:内存使用效率低。在A和B都运行的情况下,如果用户又运行了程序C,而程序C需要20M大小的内存才能运行,而此时系统只剩下8M的空间可供使用,所以此时系统必须在已运行的程序中选择一个将该程序的数据暂时拷贝到硬盘上,释放出部分空间来供程序C使用,然后再将程序C的数据全部装入内存中运行。可以想象得到,在这个过程中,有大量的数据在装入装出,导致效率十分低下。

问题3:程序运行的地址不确定。当内存中的剩余空间可以满足程序C的要求后,操作系统会在剩余空间中随机分配一段连续的20M大小的空间给程序C使用,因为是随机分配的,所以程序运行的地址是不确定的。但是我们的某些硬件是需要在固定的地址上去开始运行的,但是如果这个地址后边被我们的程序占有,那么我们对这块内存的修改,就可能导致某些硬件不可用了。

为了解决上述问题,人们想到了一种变通的方法,就是增加一个中间层,利用一种间接的地址访问方法访问物理内存。按照这种方法,程序中访问的内存地址不再是实际的物理内存地址,而是一个虚拟地址,然后由操作系统将这个虚拟地址映射到适当的物理内存地址上。这样,只要操作系统处理好虚拟地址到物理内存地址的映射,就可以保证不同的程序最终访问的内存地址位于不同的区域,彼此没有重叠,就可以达到内存地址空间隔离的效果。

具体参考1.1 虚拟地址和物理地址

虚拟内存(Virtual memory)是一个允许程序从逻辑角度访问内存的功能,而无需考虑物理可用的主内存量

一个程序通过虚拟地址中的页码数以及页内偏移量来引用一个单词。进程的每个页码都可以在主存中的任何地方,分页系统提供了程序中使用的虚拟地址与主存储器中的实际地址或者物理地址之间的动态映射。

利用可用的动态映射硬件,下一步的逻辑步骤是消除对进程的所有页面同时驻留在主内存中的要求。 进程的所有页面都保留在磁盘上。 执行某个进程时,其某些页面位于主内存中。 如果引用的页面不在主内存中,则内存管理硬件会检测到该页面并安排丢失的页面要加载。

操作系统-Operating-System第三章02:地址空间和地址生成

存储由可直接寻址(通过机器指令)的主存储器和低速辅助存储器组成,可通过将块加载到主存储器来间接访问。 地址转换硬件内存管理单元MMU)位于处理器和内存之间。 程序使用虚拟地址引用位置,虚拟地址被映射到实际的主内存地址中。 如果对不在真实存储器中的虚拟地址进行引用,则将主存的一部分内容交换到辅助存储器中,并交换所需的数据块。在此活动期间,生成地址引用的过程必须暂停。 OS设计人员需要开发一种地址转换机制,该机制几乎不会产生任何开销,并且需要一种存储分配策略,以最大程度地减少内存级别之间的流量。

操作系统-Operating-System第三章02:地址空间和地址生成

地址生成

CPU

  1. ALU(Arithmetic&logical Unit)需要对逻辑地址中的内容进行读写操作;
  2. MMU(Memory Management Unit)将逻辑地址转换成物理地址;
  3. CPU给总线发送物理地址请求

内存

  1. 发送物理地址的内容给CPU
  2. 或接收CPU数据到物理地址

操作系统

建立逻辑地址LA(Logic Address)和物理地址PA(Physical Address)的映射

操作系统-Operating-System第三章02:地址空间和地址生成

逻辑地址生成

从最开始的脚本foo()开始,首先进行编译,得到机器可以识别的汇编语言。之后进行编译,将函数名从字符串变为地址75;而这个函数可能是某个函数库中的成员,而函数库的位置程序是不知道的,这就需要链接,将需要的文件拼接起来,此时可能在原先的代码前面插入了一段代码,比如地址偏移了100,此时函数地址变为了175;最后需要进行程序加载即重定位,因为这段程序不一定是从逻辑地址的0位开始的,比如是从1000开始的,那么函数地址成为1175。经历了这么多步骤之后,生成的1175才是我们需要的逻辑地址。

操作系统-Operating-System第三章02:地址空间和地址生成

逻辑地址生成的时间点

  1. 编译时。假定起始地址已知,也就是我知道我的程序要放在哪,那么编译时就可以生成逻辑地址但是当起始地址改变时,比如修改了代码,必须重新编译。老旧的功能机买来之后不允许下载软件,这种情况下地址通常就是写死的。
  2. 加载时。当你修改了代码或者安装了各种新的软件,写程序时是无法知道新的代码存储在什么地址的。也就是编译时起始位置未知时编译器需生成可重定位的代码等到加载时重定位生成绝对地址。通常在可执行文件的前面有一个重定位表,加载的时候改成绝对地址,程序就可以跑了。
  3. 执行时。执行到这条指令之前一直使用相对地址,当执行到这一条指令的时候,才可以去知道它确切访问的地址。这种情况出现在使用虚拟存储的系统里。优点就是在程序执行过程中就可以将代码移动,而前面两种生成时机都不可以。但是需要地址转化硬件支持,相对较麻烦。

地址检查

CPU执行到某条指令,得到它的逻辑地址,首先根据逻辑地址判断所在它的偏移量是否在所在段(比如数据段)的长度之内,如果超出了段长度,认为是非法请求,否则认为是合法的。此时加上段基址得到物理地址,进行访问。在这个过程中操作系统要做的就是设置段起始地址和最大逻辑地址空间(段长度)。

操作系统-Operating-System第三章02:地址空间和地址生成