物理和虚拟寻址
内存管理单元(MMU)
地址空间
非负整数地址的有序集合
- 虚拟地址空间
- 物理地址空间
虚拟内存作为缓存的工具
- 未分配
- 缓存的
- 未缓存的
DRAM缓存的组织结构
与存储器的层次结构
页表
页命中
系统通过页表获取到物理内存当中的页
缺页
物理内存缓存不命中成为缺页
分配页面
局部性原理
局部性原理是指CPU访问存储器时,无论是存取指令还是存取数据,所访问的存储单元都趋于聚集在一个较小的连续区域中。
虚拟内存作为内存管理工具
- 简化链接
- 简化加载
- 简化共享
- 简化内存分配
虚拟内存作为内存保护的工具
地址翻译
基本参数:
符号 | 描述 |
---|---|
$N=2^n$ | 虚拟地址空间中的地址数量 |
$M=2^m$ | 物理地址空间中的地址数量 |
$P=2^p$ | 页的大小(字节) |
虚拟地址(VA)的组成部分:
符号 | 描述 |
---|---|
VPO | 虚拟页面偏移量(字节) |
VPN | 虚拟页号 |
TLBI | TLB索引 |
TLBT | TLB标记 |
物理地址(PA)的组成部分:
符号 | 描述 |
---|---|
PPO | 物理页面偏移量(字节) |
PPN | 物理页号 |
CO | 缓冲块内的字节偏移量 |
CI | 高速缓存索引 |
CT | 高速缓存标记 |
- 通过虚拟地址找到页表(page table)中对应的条目
- 检查有效位(valid bit),是否需要触发页错误(page fault)
- 然后根据页表中的物理页编号找到内存中的对应地址
- 最后把虚拟页偏移和前面的实际地址拼起来,就是最终的物理地址
当页面命中时:
当缺页时:
结合高速缓存和虚拟内存
利用TLB加速地址翻译
多级页表
内存映射
将虚拟内存区域与一个磁盘对象关联起来,以初始化这个虚拟内存区域的内容
- 普通文件
- 匿名文件
共享对象
- 写时复制
用户级内存映射
void *mmap(void *start,size_t length,int prot,
int flags,int fd,off_t offset)
动态内存分配
显式分配
程序员手动释放内存
隐式分配
垃圾收集器回收
malloc与free函数
void *malloc(size_t size);
void free(void *p);
使用动态内存分配的原因
- 程序运行的未知性
分配器的要求和目标
- 处理任意请求序列
- 立即响应请求
- 只使用堆
- 对齐块
- 不修改已分配的块
目标
- 最大化吞吐率
- 最大化内存利用率
碎片
- 内部碎片
- 外部碎片
实现问题
- 如何记录空闲块
- 如何选择一个合适的空闲块放置一个新分配的块
- 如何处理空闲块被分配后剩余的部分
- 如何处理一个被释放的块
隐式空闲链表
放置已分配的块
- 首次适配
- 下次适配
- 最佳适配
分割空闲块
将空闲块分为两部分,一部分变成分配块,另一部分变成空闲块
获取额外的堆内存
合并空闲块
假碎片
- 块合并
带边界标记的合并
显式空闲链表
分离的空闲链表
- 简单分离存储
- 分离适配
- 伙伴系统
垃圾收集
C程序常见的与内存有关的错误
- 间接引用坏指针
- 读未初始化的内存
- 栈缓冲区溢出
- 假设指针与指针所指向的对象大小相同
- 错位错误
- 引用了指针,而不是指针所指的对象
- 误解指针运算
- 引用不存在的变量
- 引用空闲堆块中的数据
- 内存泄漏