物理和虚拟寻址


内存管理单元(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程序常见的与内存有关的错误
- 间接引用坏指针
 - 读未初始化的内存
 - 栈缓冲区溢出
 - 假设指针与指针所指向的对象大小相同
 - 错位错误
 - 引用了指针,而不是指针所指的对象
 - 误解指针运算
 - 引用不存在的变量
 - 引用空闲堆块中的数据
 - 内存泄漏