王道操作系统笔记,视频链接:3.1.1.1 内存的基础知识
知识总览
- 内存的基础知识:
- 什么是内存,有何作用
- 进程运行的基本原理:
- 指令的工作原理
- 逻辑地址 vs 物理地址
- 如何实现地址转换
- 从写程序到程序运行的过程
什么是内存?有何作用?
- 内存可存放数据。程序执行前需要先放到内存中才能被CPU处理 ------缓和CPU与硬盘之间的速度矛盾。
- 因为CPU处理速度很快,但是硬盘读取速度很慢,
- 不放内存CPU的速度会被硬盘拖累。
- 思考:在多道程序环境下,系统中会有多个程序并发执行,也就是说会有多个程序的数据需要同时放到内存中。那么,如何区分各个程序的数据是放在什么地方的呢?
- 方案:给内存的存储单元编地址
- 内存地址从0开始,每个地址对应一个存储单元
- 内存中也有一个一个的"小房间",每个小房间就是一个"存储单元"
- 如果计算机"按字节编址 ",则每个存储单元大小 为1字节,即1B,即8个二进制位
- 如果字长为16位 的计算机"按字编址 ",则每个存储单元大小 为1个字 ;每个字的大小为16个二进制位
- 具体字长要看题目定义,有些计算机字长可能是32位、64位等
- 需要根据题目条件判断一个字占有多少比特位
补充知识:几个常用的数量单位
- 几个常用的数量单位:
2 10 2^{10} 210 = 1K (千)
2 20 2^{20} 220 = 1M (兆,百万)
2 30 2^{30} 230 = 1G (十亿,千兆) - 注:有的题目会告诉我们内存的大小,让我们确定地址长度应该是多少,
- 即要多少个二进制位才能表示相应数目的存储单元。
- 比如:一台手机/电脑 有 4GB 内存,是指该内存中可以存放 4 ∗ 2 30 4*2^{30} 4∗230个字节。
- 如果是按字节编址的话,也就是有 4 ∗ 2 30 = 2 32 4*2^{30} = 2^{32} 4∗230=232 个"小房间",
- 这么多"小房间",需要 2 32 2^{32} 232 个地址才能一一标识,
- 所以地址需要用 32 个二进制位来表示( 0 → 2 32 − 1 0 \to 2^{32}-1 0→232−1)
指令的工作原理
- 指令的工作基于"地址"。每个地址对应一个数据的存储单元
- 指令=操作码+若干参数(可能包括地址参数)
- 我们写的代码要翻译成CPU能识别的指令。
- 这些指令会告诉CPU应该去内存的哪个地址读/写数据,这个数据应该做什么样的处理。
- 假设让进程的相关内容从地址 #0 开始连续存放,
- 此时逻辑地址(相对位置)与物理地址(绝对位置)相等。
- 指令中的地址参数直接给出了变量 x 的实际存放地址(物理地址)。
- C语言程序经过编译、链接处理后,生成装入模块 ,即可执行文件(比如.exe文件):
- 程序经过编译、链接后生成的指令中指明的是逻辑地址(相对地址),
- 即:相对于进程的起始地址而言的地址
- 比如:在"从地址 #0 开始连续存放"的假设中,
- 指令0为往地址为79的存储单元中写入10,
- 那么CPU执行该指令时,就会往79地址的内存单元里写入10
- 如果不是从地址 #0 开始,程序是从 #100 开始,
- 那么直接往79地址写入就是错的,会篡改其他进程的内存空间
- 应该往100+79=179的地址写入
- 这就是逻辑地址与物理地址的区别
- 程序经过编译、链接后生成的指令中指明的是逻辑地址(相对地址),
- 问题:如何将指令中的逻辑地址转换为物理地址?
- 策略:三种装入方式
- 绝对装入
- 可重定位装入(静态重定位)
- 动态运行时装入(动态重定位)
- 策略:三种装入方式
装入的三种方法
绝对装入
- 绝对装入 :
- 在编译时,如果知道程序将放到内存中的哪个位置,编译程序将产生绝对地址的目标代码。
- 装入程序按照装入模块中的地址,将程序和数据装入内存。
- 比如:
- 如果知道装入模块要从地址为 100 的地方开始存放,
- 那么按照之前"往79写入数据"的例子,
- 就可以在编译的时候就把逻辑地址79改成物理地址179
- 绝对装入只适用于单道程序环境
- 需要知道装入模块装入内存的位置,灵活性差
- 比如程序要求放100,但是电脑放不了100,就会出问题
可重定位装入(静态重定位)
- 静态重定位 :又称可重定位装入 。
- 编译、链接后的装入模块的地址都是从0开始的,
- 指令中使用的地址、数据存放的地址都是相对于起始地址而言的逻辑地址。
- 可根据内存的当前情况,将装入模块装入到内存的适当位置。
- 装入时对地址进行"重定位",将逻辑地址变换为物理地址。
- 地址变换是在装入时一次完成的。
- 比如:
- 装入的起始物理地址为100,则在装入内存的时候,所有地址相关的参数都+100
- 静态重定位的特点是在一个作业装入内存时,
- 必须分配其要求的全部内存空间 ,
- 后续讲了虚拟存储技术会对此有更深入的了解
- 这里理解即可,不是特别重要
- 如果没有足够的内存,就不能装入该作业。
- 作业一旦进入内存后,在运行期间就不能再移动 ,也不能再申请内存空间。
- 防止移动后出错,这也是静态重定位的局限性之一。
- 必须分配其要求的全部内存空间 ,
动态运行时装入(动态重定位)
- 动态重定位 :又称动态运行时装入 。
- 编译、链接后的装入模块的地址都是从0开始的。
- 装入程序把装入模块装入内存后,并不会立即把逻辑地址转换为物理地址,
- 而是把地址转换推迟到程序真正要执行时才进行。
- 因此装入内存后所有的地址依然是逻辑地址。
- 这种方式需要一个重定位寄存器 的支持。
- 重定位寄存器存放装入模块存放的起始位置
- 在指令执行时,会将逻辑地址和重定位寄存器当中存放的起始相加,
- 这样得到的地址才是最终可以访问的地址
- 采用动态重定位时允许程序在内存中发生移动 。
- 并且可将程序分配到不连续的存储区中;
- 在程序运行前只需装入它的部分代码即可投入运行,
- 然后在程序运行期间,根据需要动态申请分配内存;
- 便于程序段的共享,可以向用户提供一个比存储空间大得多的地址空间。
- 便于程序段的共享:
- 假设有10个终端都要调用printf,
- 没有共享,那么内存里就有10份printf的机器代码
- 有共享,只用保留一份,
- 然后通过动态重定位把10个逻辑地址都映射到一个物理内存
- 提供大得多的空间:
- 假设物理内存只有4G,游戏有10G
- 程序编译时,依然假装拥有从0开始的完整超大逻辑空间(比如100G)
- 运行时,操作系统只把当前立刻要用到的极小部分(比如 10MB)装入物理内存,
- 剩下的暂时躺在硬盘里,
- 当 CPU 要访问一个逻辑地址,动态重定位发现它不在物理内存中时,
- 会触发一个"缺页中断",操作系统立刻从硬盘把那块数据调入物理内存,
- 再重新执行指令。
- 便于程序段的共享:
- PS:学习了之后的虚拟存储管理后就可以更深入理解了
从写程序到程序运行
- 编译:由编译程序将用户源代码编译成若干个目标模块
- 编译就是把高级语言翻译为机器语言
- 链接:由链接程序将编译后形成的一组目标模块,以及所需库函数链接在一起,形成一个完整的装入模块
- 图:

链接的三种方式
- 静态链接:
- 在程序运行之前,先将各目标模块及它们所需的库函数连接成一个完整的可执行文件(装入模块),之后不再拆开。
- 图:

- 装入时动态链接:
- 将各目标模块装入内存时,边装入边链接的链接方式。
- 图:

- 运行时动态链接:
- 在程序执行中需要该目标模块时,才对它进行链接。
- 其优点是便于修改和更新,便于实现对目标模块的共享。
- 比如:
- 用到模块一就放入模块一,然后链接
- 用到模块二就放入模块二,然后链接
- 没用到模块三就不管模块三
- 图:

知识回顾与重要考点

- 这个小节考察频率很低,理解即可