# 基本概念
在引入多道程序后,不同程序间会出现资源共享,其中就包括主存储器的资源共享。于是,需要给出内存管理 (memory management) 策略,防止内存的数据混乱,提高程序并发执行的效率。
内存管理的主要功能包括:
- 内存空间的分配与回收
- 地址转换
- 内存空间的扩充
- 内存共享
- 存储保护:保证各运行程序之间互不干扰。
# 程序运行的基本原理
# 程序的链接和装入
创建进程前要将程序和数据装入内存,包括编译、链接和装入三个步骤。
# 程序的链接
程序的链接包括静态链接、装入时动态链接和运行时动态链接三种方式。
# 程序的装入
程序的装入指将程序放入内存中,该过程通过装入程序完成。其包括绝对装入、可重定位装入和动态运行时装入三种方式。其中,绝对装入指将程序逻辑地址和物理地址完全对应地放入内存。而其余两种都实现了从虚拟地址到物理地址的映射,该过程称为重定位 (relocation). 可重定位装入是在程序装入时就为程序分配了完整的内存空间,并实现了全部地址的重定位,且程序不能在内存中进行移动,因此称为静态重定位;而动态运行时装入是在程序的执行过程中才实现重定位,并在程序运行中为程序动态分配内存,因此称为动态重定位。
# 内存虚拟化
CPU 生成的地址称为逻辑地址 (logical address) 或虚拟地址 (virtual address), 内存单元看到的地址存入内存地址寄存器 (memory-address register, MAR),称为物理地址 (physical address). 从虚拟地址到物理地址的映射由内存管理单元 (memory-management unit, MMU) 来完成。
# 可执行文件
可执行文件 (executable file) 是由机器语言 (二进制码) 组成的,计算机可以直接执行的文件。一般由高级语言经编译得到。
在不同操作系统中,可执行文件的格式不完全相同。例如,Windows 操作系统中的文件格式为 exe
文件,而类 Unix 系统中的可执行文件为 elf
文件。尽管文件格式不完全相同,但文件格式遵从相近的存储空间规划方式。
# EXE 文件
- DOS 头:包含 DOS 操作系统相关信息,以支持早期的 DOS 操作系统的兼容性。
- PE 头:PE 是 Portable Executable 的缩写,即可移植可执行文件。PE 头包含文件格式信息、程序入口地址、代码段、数据段、导入表、导出表、资源表、重定位表等信息。
- 节表:节是 PE 文件的最小单元,每个节对应 PE 文件中的一个区段。节表中存储了每个节的名称、大小、偏移量等信息,用于操作系统在加载程序时按照指定的顺序进行组合。
- 代码段:包含程序的指令集和函数体,用于程序的执行。
- 数据段:包含程序的全局变量、常量和字符串等静态数据。
- 初始化段 (.init 段):在程序启动时会被执行的代码段。
- 未初始化数据段 (.bss 段):包含未初始化的全局变量和静态变量。
- 导入表 (Import Table):包含程序依赖的外部库函数的名称和地址。
- 导出表 (Export Table):包含程序可供外部调用的函数名称和地址。
- 资源表 (Resource Table):包含程序使用到的资源,如图标、位图、字符串等。
- 重定位表 (Relocation Table):当程序被加载到内存中时,由于内存地址的变化,需要对程序中的地址进行修正。
# Reference
- 《Windows PE 文件格式》(微软官方文档):https://docs.microsoft.com/zh-cn/windows/win32/debug/pe-format
- 《PE 头结构分析》(博客文章):https://www.cnblogs.com/tianshui/p/9647194.html
- 《Windows PE 文件格式详解》(博客文章):https://www.cnblogs.com/vamei/archive/2012/12/01/2802811.html
- 《PE 文件格式详解》(博客文章):https://www.cnblogs.com/ycb1688/p/5708652.html
以上资料详细介绍了 PE 文件的结构和各个部分的作用,其中包括 DOS 头、PE 头、节表、代码段、数据段、初始化段、未初始化数据段、导入表、导出表、资源表和重定位表等部分。这些资料可以作为参考,更好地了解 PE 文件的构成和格式。
以上关于可执行文件的内容来自
ChatGPT
, 未经整理,准确性难以保证。
# ELF 文件
ELF 文件是类 Unix 中的可执行文件格式,全称可执行可链接文件 (executable and linkable format, ELF).
# Reference
- CSDN: 可执行目标文件的结构
- Wikipedia: Executable and Linkable Format
# 进程的内存映像
# 内存保护
在内存管理的过程中,对每一个进程,通过两个寄存器对进程所占的地址进行保护。两个寄存器为基地址寄存器 (base register, 又称重定位寄存器) 和界限地址寄存器 (limit register, 又称限长寄存器)。前者指定了该进程最小的合法物理地址,后者指定了进程占据内存的最大范围大小。两寄存器的加载都需要操作系统通过特权指令进行。
# 内存扩充
覆盖和交换技术是内存扩充的两种方法。
# 内存分配
# 连续内存分配
将多个进程同时放置于同一块内存时,若将每个进程放置于一个连续的内存区域,那么称这种方式为连续内存分配 (contiguous memory allocation)。连续内存分配包括单一连续分配、固定分区分配和动态分区分配。
# 单一连续分配
# 固定分区分配
固定分区分配就是将内存分为多个固定大小的分区 (partition)。每个分区只包含一个进程。
容易产生较多的内碎片 (internal fragmentation).
# 动态分区分配
动态分区分配又称可变分区分配,指进程在装入内存时,根据内存的实际需要,动态为进程分配内存,石洞分区的大小恰好适合进程的需要。
该方法容易产生较多的外碎片 (external fragmentation)。克服外部碎片采用紧凑 (compaction) 技术解决。
其中,寻找空闲内存的常用方法包括:
- 首次适应 (first-fit):找到大小满足要求的第一个空闲分区。通常是最好的和最快的,但在内存低地址处会出现许多小的外碎片。
- 邻近适应 (next-fit)
- 最优适应 (best-fit):找到第一个能满足要求的最小的空闲分区。
- 最差适应 (worst-fit):找到第一个能满足要求的最大的空闲分区。
# 分段
# 基本概念
分段的思想来自于 8086 设计。在 8086 中,由于寄存器长度为 16 位,而内存空间为 20 位,因此需要借助两个寄存器实现内存寻址,且内存地址的表示可以不是唯一的。因此,我们将代码中的一个逻辑块用几个段存储。例如,简单的汇编程序中也有数据段、代码段等。
严谨点地说,段 (segment) 是用户作业中具有逻辑意义的一部分。分段 (segmentation) 是以段为单位进行内存分配和管理的内存管理模式。
# 硬件
# 段表
段表 (segment table) 中的每个条目都有段基地址 (segment base) 和段界限 (segment limit)。
# 分页
# 缺页率
# 虚拟内存管理
# 虚拟内存技术
虚拟内存技术是基于局部性原理的高速缓存技术。在虚拟内存技术的支持下,系统可以为用户提供一个看起来比实际内存容量大得多的存储器,即虚拟存储器。虚拟存储器包括多次性、对换性和虚拟性三个主要特征,其中虚拟性是最重要的特征。虚拟内存技术的实现主要包括请求分页存储管理、请求分段存储管理和请求段页式存储管理三种方式。