# 13. 存储管理基础

> **课程**: 操作系统 - 存储器管理
> **核心内容**: 存储器层次结构、存储管理功能、程序编译链接与装入、地址空间

---

## 一、存储器层次结构

存储器按照速度和容量形成层次结构，越靠近CPU速度越快但容量越小、价格越高。

```
速度递增 ↑    容量递减 ↑    价格递增 ↑
┌─────────────────────────┐
│        寄存器(Register)    │  ← 最快，纳秒级，几十~几百字节
├─────────────────────────┤
│     高速缓存(Cache)        │  ← L1/L2/L3，几MB
├─────────────────────────┤
│      主存(内存/DRAM)       │  ← 几GB~几百GB，百纳秒级
├─────────────────────────┤
│      Flash/SSD             │  ← 固态存储
├─────────────────────────┤
│    磁盘缓存(Disk Cache)    │
├─────────────────────────┤
│    固定磁盘(HDD/SSD)       │  ← 几百GB~几TB，毫秒级
├─────────────────────────┤
│   可移动存储(U盘/光盘/磁带) │  ← 最慢，容量可很大
└─────────────────────────┘
```

**设计原则**: 利用**局部性原理**，将频繁访问的数据放在高速层次，较少访问的数据放在低速大容量层次。

---

## 二、存储管理的功能

操作系统存储管理需要实现以下五大核心功能：

| 功能 | 说明 |
|------|------|
| **内存分配与回收** | 为进程分配所需内存，进程结束后回收内存 |
| **地址转换** | 将程序中的逻辑地址转换为物理地址（[[14_分页存储管理\|分页]]、[[15_段式存储管理\|分段]]） |
| **内存共享** | 多个进程共享同一段代码（如共享库） |
| **内存保护** | 防止进程越界访问其他进程或内核的内存区域 |
| **内存扩充** | 通过虚拟存储技术，使程序可用空间大于实际物理内存（[[16_虚拟存储器\|虚拟存储器]]） |

---

## 三、程序的编译与链接过程

一个C语言源文件从编写到可执行，经历以下阶段：

```mermaid
flowchart LR
    A["hello.c<br/>源文件"] -->|预处理| B["hello.i<br/>预处理后"]
    B -->|编译| C["hello.s<br/>汇编文件"]
    C -->|汇编| D["hello.o<br/>目标文件(可重定位)"]
    D -->|链接| E["hello / a.out<br/>可执行文件"]
    E -->|装入| F["内存中运行的进程"]

    style A fill:#e1f5fe
    style C fill:#fff3e0
    style D fill:#fce4ec
    style E fill:#e8f5e9
    style F fill:#f3e5f5
```

### 各阶段说明

| 阶段 | 输入 | 输出 | 工具 | 说明 |
|------|------|------|------|------|
| **预处理** | `.c` | `.i` | cpp | 展开宏、头文件、条件编译 |
| **编译** | `.i` | `.s` | cc1 | 翻译为汇编语言 |
| **汇编** | `.s` | `.o` | as | 翻译为机器指令（可重定位目标文件） |
| **链接** | `.o` | 可执行文件 | ld | 合并节段、解析符号引用、重定位 |
| **装入** | 可执行文件 | 进程 | 加载器 | 将程序载入内存并创建进程 |

### 查看目标文件的节段

使用 `objdump -h` 可以查看可执行文件或目标文件中的节段(Section)信息：

```bash
gcc -c hello.c          # 编译为可重定位目标文件
objdump -h hello.o      # 查看节段头信息

gcc hello.c -o hello    # 链接为可执行文件
objdump -h hello        # 查看可执行文件的节段
```

---

## 四、可执行文件结构

可执行文件在内存中从低地址到高地址的典型布局如下：

```
高地址  ┌──────────────────┐
        │    命令行参数     │
        │   和环境变量      │
        ├──────────────────┤
        │      栈(Stack)    │  ← 局部变量、函数调用帧，向下增长 ↓
        │        ↓          │
        │                  │
        │        ↑          │
        │      堆(Heap)     │  ← malloc/new动态分配，向上增长 ↑
        ├──────────────────┤
        │   .bss 段         │  ← 未初始化的全局/静态变量（不占文件空间）
        ├──────────────────┤
        │   .data 段        │  ← 已初始化的全局变量和静态变量
        ├──────────────────┤
        │   .rodata 段      │  ← 只读数据（如字符串常量）
        ├──────────────────┤
        │   .text 段        │  ← 可执行代码（机器指令）
低地址  └──────────────────┘
```

| 段名 | 内容 | 是否可写 | 说明 |
|------|------|---------|------|
| `.text` | 机器指令代码 | 只读/只执行 | 程序的可执行代码 |
| `.rodata` | 只读数据 | 只读 | 字符串常量、`const`变量 |
| `.data` | 已初始化全局变量 | 可读写 | 有初始值的全局和静态变量 |
| `.bss` | 未初始化全局变量 | 可读写 | 不占文件空间，装入时清零 |
| 堆(Heap) | 动态分配内存 | 可读写 | `malloc`/`new` 分配 |
| 栈(Stack) | 函数调用帧 | 可读写 | 局部变量、返回地址等 |

---

## 五、逻辑地址与物理地址

### 核心概念

| 术语 | 别名 | 说明 |
|------|------|------|
| **逻辑地址** | 虚拟地址(VA)、相对地址 | 程序中使用的地址，从0开始编址 |
| **物理地址** | PA、绝对地址 | 内存硬件中的实际地址 |

> **关键**: 程序中的地址（逻辑地址）**不等于**内存中的实际地址（物理地址）。地址转换由硬件（[[01_系统运行机制#四、存储器管理硬件——MMU|MMU]]）和操作系统配合完成。

### 逻辑地址空间与物理地址空间

```mermaid
flowchart TB
    subgraph VA["逻辑地址空间 (虚拟)"]
        direction TB
        V0["地址 0"]
        V1["..."]
        V2["地址 2^v - 1"]
    end

    subgraph PA["物理地址空间 (实际)"]
        direction TB
        P0["地址 0"]
        P1["..."]
        P2["地址 2^p - 1"]
    end

    VA -->|"地址映射<br/>(页表/段表)"| PA

    style VA fill:#e3f2fd,stroke:#1976d2
    style PA fill:#fff8e1,stroke:#f9a825
```

- **虚拟地址空间**: 大小为 $2^v$ 字节，其中 $v$ 是虚拟地址的位数
- **物理地址空间**: 大小为 $2^p$ 字节，其中 $p$ 是物理地址的位数
- 通常 $v \geq p$（虚拟地址空间可以大于物理内存），这就是[[16_虚拟存储器|虚拟存储器]]的基础

### 引入逻辑地址的好处

1. **进程隔离**: 每个进程拥有独立的虚拟地址空间，互不干扰
2. **提高内存利用率**: 可以使用[[16_虚拟存储器|虚拟存储器]]技术
3. **内存保护**: 通过地址转换实现访问权限控制

---

## 六、内核空间与用户空间

操作系统将每个进程的虚拟地址空间划分为**用户空间**和**内核空间**两部分：

```mermaid
block-beta
    columns 1
    block:linux["Linux 进程地址空间 (4GB)"]
        columns 1
        block:kernel_linux["内核空间 (高 1GB)"]
            k1["内核代码、数据、内核栈等"]
        end
        block:user_linux["用户空间 (低 3GB)"]
            u1["栈 ↓"]
            u2["..."]
            u3["堆 ↑"]
            u4[".bss / .data / .rodata / .text"]
        end
    end
    block:win["Windows 进程地址空间 (4GB)"]
        columns 1
        block:kernel_win["内核空间 (高 2GB)"]
            k2["内核代码、驱动等"]
        end
        block:user_win["用户空间 (低 2GB)"]
            u5["用户程序空间"]
        end
    end
```

| 操作系统 | 用户空间 | 内核空间 | 说明 |
|---------|---------|---------|------|
| **Linux (32位)** | 0 ~ 3GB (低3GB) | 3GB ~ 4GB (高1GB) | 通过`PAGE_OFFSET`划分 |
| **Windows (32位)** | 0 ~ 2GB (低2GB) | 2GB ~ 4GB (高2GB) | 可通过`/3GB`启动参数调整为3:1 |

**重要规则**: 用户态程序**不能**直接访问内核空间的地址，否则触发保护异常。内核态代码可以访问整个地址空间。

---

## 七、MMU 与内存保护

**MMU (Memory Management Unit)** 是CPU中负责地址转换和内存保护的硬件单元。

### 地址转换流程

```mermaid
flowchart LR
    CPU["CPU 发出<br/>虚拟地址(VA)"] --> MMU["MMU<br/>地址转换"]
    MMU --> PA["物理地址(PA)"]
    PA --> MEM["访问内存"]

    style MMU fill:#ffcdd2,stroke:#c62828
```

### 页表保护机制

MMU通过页表中的**U/S位**和CPU的**模式位**配合实现内存保护：

| 页表U/S位 | 含义 |
|-----------|------|
| **U=0 (Supervisor)** | 内核态页面，只有内核可以访问 |
| **U=1 (User)** | 用户态页面，用户态和内核态均可访问 |

| CPU模式 | 可访问的页面 |
|---------|-------------|
| **用户模式 (User mode)** | 只能访问 U=1 的页面 |
| **内核模式 (Kernel mode)** | 可以访问 U=0 和 U=1 的页面 |

当用户态程序试图访问 U=0 的内核页面时，MMU会产生**保护异常（段错误/Segmentation Fault）**，终止该进程。

---

## 八、地址转换的主要方式

操作系统实现地址转换有多种方式，各有特点：

| 方式 | 原理 | 优点 | 缺点 |
|------|------|------|------|
| **重定位寄存器（基址寄存器）** | PA = VA + 基址值 | 简单 | 程序必须连续存放 |
| **静态重定位** | 装入时一次性修改所有地址 | 无需硬件支持 | 装入后不能移动 |
| **动态重定位** | 执行时通过MMU实时转换 | 灵活，支持移动 | 需要硬件支持 |
| **[[14_分页存储管理\|分页]]** | 按页划分，通过页表映射 | 消除外部碎片 | 有内部碎片、页表开销 |
| **[[15_段式存储管理\|分段]]** | 按逻辑段划分，通过段表映射 | 符合程序逻辑 | 外部碎片问题 |

---

## 九、小结

```mermaid
mindmap
  root((存储管理基础))
    存储层次
      寄存器
      Cache
      主存
      Flash
      磁盘
    管理功能
      分配回收
      地址转换
      内存共享
      内存保护
      内存扩充
    程序装入
      编译链接
      可执行文件结构
      逻辑地址vs物理地址
    地址空间
      虚拟地址空间
      物理地址空间
      内核空间/用户空间
    MMU保护
      页表U/S位
      CPU模式位
```

---

## 关联笔记

- [[01_系统运行机制]] — CPU工作模式（用户态/内核态）与MMU硬件基础
- [[14_分页存储管理]] — 分页式地址转换的详细实现
- [[15_段式存储管理]] — 分段式地址转换
- [[16_虚拟存储器]] — 虚拟存储器的实现原理

---

**上一讲**: [[01_系统运行机制]]
**下一讲**: [[14_分页存储管理]]
