这篇文章主要介绍计算机的体系结构,内存的分层体系,操作系统的内存管理,地址空间和地址生成等内容。

计算机体系结构

CPU:主要完成了对整个程序或软件的执行的控制。
内存:主要是放置了程序的代码和它所处理的数据。
外设:各种外部设备配合程序更好的完成工作。

计算机体系结构

内存分层体系

内存分层体系

计算机内存包含多个层次,包括 CPU 寄存器、cache 和主存(物理内存)、硬盘(虚拟内存)。硬盘用于保存持久化数据。

  • CPU 寄存器和 cache:
    • 访问速度快,但容量小;
    • 寄存器是 CPU 内部的存储单元,用于存储临时数据和指令;
    • cache 是位于 CPU 和主存之间的高速缓存,用于加速对主存的访问
  • 主存(物理内存):
    • 存放操作系统和程序代码等数据;
    • 容量较大,但访问速度相对较慢;
    • 主存中的数据掉电会丢失。
  • 硬盘(虚拟内存):
    • 保存持久化数据,即使掉电也不会丢失;
    • 访问速度较慢,但容量可以很大。

操作系统会根据需要将数据从主存中放到硬盘中,以便管理数据的容量和提高运行效率。

操作系统的内存管理

目标

操作系统的内存管理到底要做什么事情呢?它有如下几个重要的目标。

  • 抽象:逻辑地址空间
    • 操作系统通过提供逻辑地址空间,将底层细节抽象化,使应用程序不需要关心物理内存和外设的管理。
  • 保护:独立地址空间
    • 为了避免应用程序相互访问彼此的地址空间,操作系统提供了独立的地址空间,将它们隔离,以保护应用程序免受破坏。
  • 共享:访问相同内存
    • 多个应用程序之间也可能进行交互,操作系统允许多个应用程序共享相同的内存空间,以实现高效和可靠的数据传递。
  • 虚拟:更多的地址空间
    • 物理内存空间有限,操作系统将必要的数据放在主存中,而将暂时不需要访问的数据临时存放在磁盘上,从而扩展了可用的地址空间。
操作系统的内存管理

在这张图中,操作系统将进程 P1、P2、P3 和 P4 的必要数据放在主存中(进程 P1 优先级最高,所有数据都在主存中),暂时不需要访问的数据(进程 P4/P2/P3 的一部分数据)临时存放在磁盘上。

从上图中,也可以看出进程(我们的程序)属于逻辑地址空间,主存和磁盘的内存空间属于物理内存空间。

内存管理的不同方法

  • 程序重定位(relocation)
  • 分段(segmentation)
  • 分页(page)
  • 虚拟内存(virtual memory)
  • 按需分页虚拟内存:目前多数操作系统(如 Linux)采用

内存管理的实现高度依赖于硬件:

  • 必须知道内存架构(与计算机存储架构紧耦合)
  • MMU(内存管理单元):硬件组件负责处理 CPU 的内存访问请求

地址空间与地址生成

地址空间定义

物理地址空间就是内存条和硬盘的空间,它的管理和控制由硬件来完成的。

逻辑地址空间就是一个运行的程序所拥有的的内存空间,是一种一维的线性地址空间(这种设计可以使得应用程序很容易的进行数据访问操作)。

逻辑地址空间的设计使得数据访问更加方便的「其中一个原因」就是:
逻辑地址空间是一维的线性地址空间,即内存地址在逻辑上是连续的。这种设计使得程序可以使用简单的地址计算来访问数据,而不需要考虑数据在内存中的实际物理位置。这样,程序员可以通过 使用相对地址来访问数据,而不需要关注数据的具体位置

地址空间

地址空间对应关系

一个运行的程序所访问的逻辑地址空间,最终都会对应着物理地址空间中的某一位置,可能放在主存中、也可能放在硬盘中。物理地址空间和逻辑地址空间通过映射关系对应起来(映射关系由操作系统进行有效地管理)。

逻辑地址生成

下图展示了逻辑地址的生成过程。

逻辑地址生成
  1. C 程序文件(.c 文件)中的函数和变量名即逻辑地址。编译器会将 C 程序转换为汇编语言程序(.s 文件),并在汇编代码中使用符号来代表函数和变量的名字。
  2. 汇编语言程序(.s 文件)中的符号仍然是逻辑地址。编译器将汇编语言程序转换为机器语言程序(.o 文件),并将变量和函数的符号名转换为逻辑地址。
  3. 链接器(linker)将多个机器语言程序(.o 文件)链接在一起,形成一个单一的可执行文件(.exe 文件)。链接器会为每个变量和函数分配唯一的逻辑地址,并解决不同文件之间的符号引用关系。
  4. 加载器(loader)将可执行文件(.exe 文件)加载到内存中运行。加载器会将可执行文件的代码和数据加载到内存的适当位置,并进行地址的重定位。由于加载过程中会存在一定的偏移量,程序需要根据偏移量来进行正确的数据访问和指令操作。

逻辑地址生成的过程可以在编译器、链接器和加载器等工具的协同作用下完成,操作系统通常不需要干预该过程。这样的设计使得程序员可以方便地使用符号和逻辑地址进行开发,而无需关心实际的物理地址和内存布局。

物理地址生成

下图展示了物理地址的生成过程。

物理地址生成

物理地址的生成过程是由操作系统的内存管理单元(MMU)来完成的。MMU 负责将逻辑地址转换为物理地址,使得程序能够正确地访问内存。

  1. 分页:操作系统将整个物理内存划分为固定大小的页面(page),通常是 4KB 或者更大的大小。同时,逻辑地址空间也被划分为相同大小的页面。
  2. 页表:每个进程都有一个页表,用于记录逻辑地址与物理地址的映射关系。页表的每个表项(entry)记录了逻辑页面号与物理页面号的对应关系。
  3. 地址转换:当程序访问某个逻辑地址时,MMU 会根据页表中的映射关系将逻辑地址转换为物理地址。具体的转换过程是 通过逻辑地址中的页号(page number)和页内偏移(page offset)进行计算得到:paddr = (page_num << PAGE_SHIFT(12)) + (vaddr % PAGE_SIZE(1U << 12))。
  4. 物理地址访问:得到物理地址后,程序就可以使用该地址直接访问内存中的数据或指令。

需要注意的是,物理地址生成的过程是 在程序运行时 由硬件执行的,而不是编译、链接或加载阶段进行的。操作系统会在程序加载到内存时设置好页表,并将页表的基地址告诉 MMU,以便进行地址转换。这样,程序在运行过程中,无需关心物理地址的生成和管理,而是通过逻辑地址进行访问。

为了加快地址转换的速度,操作系统会将逻辑地址到物理地址的映射关系存储在内存中,并由 CPU 进行缓存。

具体来说,当 CPU 首次访问某个逻辑地址时,MMU 会根据页表将逻辑地址转换为物理地址,并将该映射关系存储在一个特殊的高速缓存中,称为转换后备缓冲器(Translation Lookaside Buffer,简称 TLB)。TLB 是一种硬件缓存,用于临时存储逻辑地址到物理地址的映射关系。
当程序再次访问相同的逻辑地址时,CPU 会首先检查 TLB 中是否存在该映射关系:

  • 如果存在,则可以直接从 TLB 中获取物理地址,从而避免了访问内存的开销;
  • 如果 TLB 中没有找到对应的映射关系,则需要通过页表来进行地址转换(上述序号 2 和 3),并将新的映射关系存储到 TLB 中,以便下次访问时可以直接使用。

地址安全检查

为了避免应用程序相互访问彼此的地址空间,操作系统提供了独立的地址空间,将它们隔离,以保护应用程序免受破坏。

地址安全检查

地址安全检查使应用程序在内存中正常执行,同时保证在内存中不同的应用程序之间不会相互破坏。

参考资料:

  1. https://blog.csdn.net/weixin_53407527/article/details/124930556