logo
导航

01. 基本概念

内存管理基础

内存管理是操作系统的核心功能之一,负责管理计算机的主存储器,为进程分配和回收内存空间,确保内存的安全使用。

程序装入与链接

程序装入过程

程序从外存调入内存的过程称为程序装入,主要包括以下步骤:

  1. 编译阶段:将源代码编译成目标代码
  2. 链接阶段:将多个目标模块链接成可执行程序
  3. 装入阶段:将可执行程序装入内存

链接方式

1. 静态链接

// 编译时链接所有库函数
gcc -static program.c -o program
  • 优点:执行速度快,不依赖外部库
  • 缺点:可执行文件大,内存利用率低

2. 动态链接

// 运行时链接库函数
gcc program.c -o program
  • 优点:节省内存,便于库函数更新
  • 缺点:执行速度稍慢,依赖外部库

3. 运行时动态链接

// 程序运行时根据需要加载库
dlopen("libmath.so", RTLD_LAZY);

装入方式

1. 绝对装入

  • 程序装入到指定的绝对地址
  • 适用于单道程序系统
  • 地址在编译时确定

2. 可重定位装入

  • 程序装入到内存的任意位置
  • 需要重定位寄存器
  • 支持多道程序设计

3. 动态运行时装入

  • 程序在运行时才确定装入位置
  • 支持虚拟内存管理
  • 地址转换延迟到访问时进行

逻辑地址与物理地址空间

地址空间概念

逻辑地址空间

  • 由 CPU 生成的地址
  • 从 0 开始的连续地址空间
  • 程序员可见的地址

物理地址空间

  • 内存单元的实际地址
  • 可能不连续
  • 硬件可见的地址

地址转换过程

逻辑地址 → 地址转换机构 → 物理地址

地址转换机构

  1. 重定位寄存器:基址寄存器
  2. 页表:分页系统中的地址映射
  3. 段表:分段系统中的地址映射

地址转换示例

基址重定位

// 逻辑地址:1000
// 基址寄存器:5000
// 物理地址 = 逻辑地址 + 基址 = 1000 + 5000 = 6000

分页地址转换

// 逻辑地址:0x1234
// 页号:0x12,页内偏移:0x34
// 通过页表查找物理块号
// 物理地址 = 物理块号 × 页大小 + 页内偏移

内存保护

保护机制

1. 界限寄存器保护

// 基址寄存器:5000
// 界限寄存器:2000
// 有效地址范围:[5000, 7000)
if (address < base || address >= base + limit) {
    // 地址越界,产生保护性中断
    raise_protection_fault();
}

2. 页表保护位

typedef struct {
    int frame_number;    // 物理块号
    int valid;          // 有效位
    int read;           // 读权限
    int write;          // 写权限
    int execute;        // 执行权限
} PageTableEntry;

3. 段表保护位

typedef struct {
    int base;           // 段基址
    int limit;          // 段长度
    int read;           // 读权限
    int write;          // 写权限
    int execute;        // 执行权限
} SegmentTableEntry;

保护类型

1. 越界保护

  • 检查访问地址是否在允许范围内
  • 防止访问其他进程的内存空间

2. 权限保护

  • 读权限:允许读取数据
  • 写权限:允许修改数据
  • 执行权限:允许执行代码

3. 共享保护

  • 共享页面的访问控制
  • 确保共享数据的一致性

内存保护实现

硬件支持

// MMU(内存管理单元)实现地址转换和保护
typedef struct {
    int base_register;      // 基址寄存器
    int limit_register;     // 界限寄存器
    int protection_bits;    // 保护位
} MMU;

软件支持

// 操作系统内存管理
void memory_protection_check(int address, int access_type) {
    if (!is_valid_address(address)) {
        raise_segmentation_fault();
    }

    if (!has_permission(address, access_type)) {
        raise_protection_fault();
    }
}

内存管理的基本要求

1. 地址转换

  • 将逻辑地址转换为物理地址
  • 支持动态地址转换
  • 高效的地址转换机制

2. 内存分配

  • 为进程分配所需的内存空间
  • 支持动态内存分配
  • 高效的内存分配算法

3. 内存回收

  • 及时回收不再使用的内存
  • 防止内存泄漏
  • 支持内存压缩

4. 内存保护

  • 防止进程间相互干扰
  • 保护系统内存不被破坏
  • 支持内存访问权限控制

5. 内存共享

  • 支持进程间内存共享
  • 提高内存利用率
  • 支持共享库

内存管理的发展历程

1. 单道程序设计

  • 内存只分配给一个程序
  • 简单的内存管理
  • 内存利用率低

2. 多道程序设计

  • 内存分配给多个程序
  • 需要内存保护机制
  • 支持程序切换

3. 分页系统

  • 将内存划分为固定大小的页
  • 支持虚拟内存
  • 提高内存利用率

4. 分段系统

  • 按逻辑模块分配内存
  • 支持动态内存分配
  • 便于程序模块化

5. 段页式系统

  • 结合分段和分页的优点
  • 支持复杂的地址空间
  • 现代操作系统的主流方式

内存管理性能指标

1. 内存利用率

内存利用率 = 已使用内存 / 总内存 × 100%

2. 地址转换开销

  • 页表访问次数
  • TLB 命中率
  • 地址转换延迟

3. 内存分配效率

  • 分配算法的时间复杂度
  • 内存碎片率
  • 分配成功率

4. 内存保护开销

  • 保护检查的时间开销
  • 保护故障的频率
  • 保护机制的可靠性

实际应用示例

Linux 内存管理

进程地址空间

// 进程的虚拟地址空间布局
struct mm_struct {
    unsigned long start_code;    // 代码段起始地址
    unsigned long end_code;      // 代码段结束地址
    unsigned long start_data;    // 数据段起始地址
    unsigned long end_data;      // 数据段结束地址
    unsigned long start_brk;     // 堆起始地址
    unsigned long brk;           // 堆结束地址
    unsigned long start_stack;   // 栈起始地址
};

内存映射

// 内存映射文件
void *mmap(void *addr, size_t length, int prot, int flags,
           int fd, off_t offset);

Windows 内存管理

虚拟地址空间

  • 32 位系统:4GB 虚拟地址空间
  • 64 位系统:16TB 虚拟地址空间
  • 支持内存映射文件

内存保护

  • 页面级保护
  • 段级保护
  • 进程级保护

总结

内存管理基础是理解操作系统内存管理机制的关键。通过掌握程序装入与链接、地址空间转换和内存保护等基本概念,为学习更高级的内存管理技术奠定基础。现代操作系统的内存管理越来越复杂,但基本原理仍然是地址转换、内存分配、内存保护和内存共享。