一、进程的地址空间

  • OS提供了一个物理内存的抽象:地址空间
    • 包含运行程序的所有内存状态
      • 代码:存储指令
      • 栈(向上增长):局部变量、函数参数、返回值等
      • 堆(向下增长):动态分配的内存:malloc、new

程序并不是真正存储在物理地址的0-16KB的地方

进程看到的都是虚拟地址

enter image description here

二、内存相关API

显式的申请内存:malloc()

void* malloc(size_t size)
  • 在堆上申请内存空间
    • size参数为申请空间的大小(byte)
    • 成功则返回指向分配内存的指针,失败则返回空指针

释放内存:free()

void free(void* ptr)
  • 释放调用malloc分配的内存
    • 参数为调用malloc返回的指针(释放内存大小不是用户决定)
int *pi; //local variable
pi = (int *)malloc(sizeof(int) * 4)
free(pi)

可能错误:

  • 没有申请足够的内存:导致缓冲区溢出(buffer overflow)
  • 忘记初始化分配的内存:导致读取未知数据
  • 忘记释放内存:导致内存泄露(memory leak)
  • 在用完之前释放内存:导致悬挂指针(dangling pointer)
  • 错误的调用free:导致无效释放(invalid free)

其他申请内存API:calloc()

void *calloc(size_t num, size_t size)
  • 在堆上申请内存空间,并且将其置0
  • 可以防止忘记初始化分配的内存

其他申请内存API:realloc()

void *realloc(void *ptr, size_t size)
  • 可以增大分配内存空间(当需要添加一些信息)
    • 参数ptr传入由malloc, calloc或者realloc分配的内存块指针
    • 参数size表示新的内存大小
    • 返回值可能与ptr相同,也可能不同

相关系统调用:brk,sbrk,mmap

enter image description here

enter image description here

enter image description here

三、“基址+界限”地址转换

  • 如何实现高效、灵活的内存虚拟化?
    • 如何提供应用程序所需的灵活性?
    • 如何保持控制应用程序可访问的内存位置?
    • 如何高效的实现这些?

高效和控制是现代操作系统的两个主要目标, 分别对应性能和可靠性

  • 基于硬件的地址转换(受限直接访问)

    • 硬件对内存访问进行处理 (高效)
    • 将指令中的虚拟地址(virtual address)转换为实际内存的物理地址 (physical address)
    • OS需要在关键位置介入,设置好硬件以完成正确的转换 (控制)
    • OS需要记录和管理哪些信息?
  • 假设1:地址空间小于实际物理内存

  • 假设2:假设每个地址空间大小一

enter image description here

  • pc=128, 获取指令,地址是 32896 = 128 + 32𝐾𝐵(𝑏𝑎𝑠𝑒)
  • 执行指令
    • 从虚拟地址15K开始加载,地址是47𝐾𝐵 =15𝐾𝐵 + 32𝐾𝐵(𝑏𝑎𝑠𝑒)

enter image description here

  • 硬件需要提供什么支持?
    • 特权模式
    • 基地址/界限寄存器
    • 虚拟地址转换/越界检查
    • 注册异常程序
    • 触发异常
  • OS需要做什么?(三个关键时刻)
  • 当进程创建的时候,OS必须寻找内存空间

enter image description here

  • 当进程终止的时候,OS必须回收内存空间

enter image description here

  • 当进程切换的时候,OS需要保存和恢复基地址和界限

enter image description here

小结

  • 进程的地址空间
    • 内存虚拟化、(虚拟)地址空间
  • 内存相关API
    • 用法和可能的问题
  • “基地址+界限”地址转换
    • 地址转换机制、基址/界限寄存器、OS需要做什么