Lec17 Virtual memory for applications
MIT6.S081 Lecture17 Virtual memory for applications学习笔记
1.应用程序使用虚拟内存所需要的特性#
- 需要支持trap:使内核中的Page Fault可以传播到用户空间中,用户空间中的handler处理相应Page Fault,之后返回内核恢复指令执行;
- Prot1:降低一个内存Page的读写权限(accessibility),如从可读写变为只读权限;
- ProtN:等效调用N次Prot1,但是使用ProtN可以平摊修改TLE的成本(一次可以修改多位),而不是像多次调用那样每次都需要修改TLE;
- Unprot:提升一个内存Page的读写权限(accessibility);
- Dirty:查看某个内存Page是否被修改过(Dirty);
- map2:将一段物理内存映射到两份虚拟内存上,即两个虚拟内存地址指向同一个物理内存,且两份虚拟内存有不同的读写权限(accessibility)。
2.支持应用程序使用虚拟内存的系统调用#
(1)mmap#
mmap, munmap - map or unmap files or devices into memoryplaintextvoid *mmap(size_t length;
void addr[length], size_t length, int prot, int flags,
int fd, off_t offset);
int munmap(size_t length;
void addr[length], size_t length);c- addr:想要映射到的目标地址,若为null,则内核选择一个地址完成映射
- length:地址段长度
- prot:读写权限设置位(Protection bit)
- flags:指定不同的效果
- fd:传入的对象(文件描述符)
- offset:文件中的偏移量
函数作用:将文件中从起始位置+offset的地方开始,映射到指定的目标地址,长度为length,实现把文件中的内容加载到内存地址空间中。
(2)mprotect#
int mprotect(size_t size;
void addr[size], size_t size, int prot);
c函数作用:对虚拟内存设置权限。
(3)sigaction#
int sigaction(int signum,
const struct sigaction *_Nullable restrict act,
struct sigaction *_Nullable restrict oldact);c具体用法可查看:sigaction(2) - Linux manual page ↗
函数作用:当特定的signal发生时,可以调用预先设定好的handler,在handler中响应事件。
(4)系统调用与特性之间的关系#
- trap对应的是sigaction系统调用。
- Prot1,ProtN和Unprot可以使用mprotect系统调用来实现。mprotect足够的灵活,你可以用它来修改一个Page的权限,也可以用它来修改多个Page的权限。当修改多个Page的权限时,可以获得只清除一次TLB的好处。
- 查看Page的Dirty位要稍微复杂点,并没有一个直接的系统调用实现这个特性,不过你可以使用一些技巧完成它,我稍后会介绍它。
- map2也没有一个系统调用能直接对应它,通过多次调用mmap,你可以实现map2特性。
3.虚拟内存系统如何支持用户应用程序#
(1)VMAs#
VMAs记录的是一些连续内存地址段(section)的信息。在地址空间中,包含多个section,每个section又包含了一段连续的地址段。每个section对应一个VMA对象。每个section都有相同的权限,并且对应到同一个VMA对象。
(2)User level trap#
流程:segfault事件发生—>传播到user space—>通过用户空间的一个upcall运行handler—>在handler中处理事件—>返回内核代码—> 从内核返回之前被中断的进程
4.构建大的缓存表#
**要求:**从0~n,表单中记录了一些费时的函数计算结果,可以通过参数i查询对应的结果。需要预先计算好结果并存储在表单中供多次查询使用,表单很大,但是只可以用一个page的物理内存!
实现思路:
- 分配一个大的虚拟地址段,但是不分配任何物理内存
- 查询参数i对应的结果,导致pagefault
- 发生pagefault后,针对访问目标虚拟地址分配物理内存page,并计算这个page上所有参数对应的结果
- 消耗完物理内存后回收物理内存page并分配给新的虚拟内存地址段
5.Baker’s Real-Time Copying Garbage Collector#
Garbage Collector可以实现自动释放内存的功能,GC可以利用用户空间虚拟内特性。
Coping GC#
内存空间分为:from空间;to空间
空闲时先从from空间申请内存,假设申请了一个类似树的数据结构。

Coping GC的主要思想是将正在使用的对象拷贝到to空间去,从根节点(root)开始拷贝(假设只有一个根节点),递归遍历并复制所有可达对象到to空间,同时在每个可达对象的from空间原地址留下forwarding pointer,记录对象已迁移的目标地址。直到from空间变为空闲区域!
Baker增量GC#
- 不暂停程序,将 GC 拆分为多个小步骤:
- 初始:仅复制根对象(root)到 to 空间(未更新其内部指针)。
- 每次内存分配(
new)时:GC 扫描并复制若干对象。 - 每次指针解引用(dereference)时:
- 检查目标对象是否仍在 from 空间;
- 若是,则立即 forward 到 to 空间,并更新指针。
- 确保运行时所有活跃指针最终都指向 to 空间对象。
问题:
- dereference需要额外的步骤,增加开销
- 不容易并行运行GC
6.使用虚拟内存特性的GC#
基本思想:
(1)地址划分#
每个半空间(from / to)进一步分为两个逻辑区域:
-
unscanned:对象已复制但未扫描(指针未更新)
-
scanned:对象已完成扫描(指针已修正,可安全访问)
(2)权限控制+pagefault#
- unscanned 区域的页权限设为
None(不可访问)- 应用程序首次访问 → 触发 Page Fault
- Page Fault Handler 中:
- GC 扫描该页内所有对象
- 将其指针指向的对象从 from → to 空间 forward
- 将该页标记为 scanned,并通过
mprotect恢复读写权限
- 此后应用可安全访问,无需运行时指针检查
(3)并发安全机制#
- 应用程序只能访问scanned页面(即unscanned页面权限设置为None)
- 但是GC又能够读写unscanned页面(即unscanned页面权限设置为RW)
所以如何实现unscannned页面对于不同的进程有不同的权限呢?map2!
即将同一物理地址空间页映射到两份不同的虚拟地址空间段上,具有不同的权限。
7.虚拟内存特性的GC代码展示#
使用api:new和readptr
readptr会检查指针是否在from空间,如果在from空间,则把它指向对象拷贝到to空间中。
1. 内存布局设置(初始化阶段)#
- 使用
shm_open创建一个匿名共享内存对象(本质是 POSIX 共享内存段)。 - 用
ftruncate将其扩展到足够容纳 from-space + to-space 的大小。 - 通过 两次
mmap将同一物理内存映射到两个不同的虚拟地址区间:- 一次映射给 mutator(应用程序视角);
- 一次映射给 GC(GC 视角,拥有完整读写权限)。

2. 初始状态:to-space 对 mutator 不可访问#
- 在
flip()(切换 from/to 空间)时,GC 调用mprotect(to_region, PROT_NONE),使得 mutator 无法访问 to-space 中的任何页。 - GC 将根对象(如
root_head,root_last)复制到 to-space —— 但 mutator 不能访问它们。 - 下面代码中的to是应用程序视角下to空间的虚拟地址。

3. Mutator 访问触发 Page Fault#
- 当 mutator 尝试访问 to-space 中的对象(例如通过指针解引用),由于该页被设为
PROT_NONE,会触发 Page Fault。 - 操作系统将控制权交给 自定义的 Page Fault handler(通过信号如
SIGSEGV或用户态缺页处理机制实现,或在内核模块中拦截)。
4. Page Fault Handler 的关键逻辑#

// 假设 fault_addr 是触发缺页的地址
page = get_page(fault_addr);
// 步骤 1: GC 扫描该页中的所有对象(使用 GC 的映射地址)
scan_objects_in_page(page, gc_mapped_addr);
// 步骤 2: 将该页对 mutator 设为可读写
mprotect(mutator_mapped_addr_of_page, size, PROT_READ | PROT_WRITE);c必须先完成 scan(确保页内所有对象已被正确复制、指针已更新),再开放访问权限。
5. GC 的视角:始终可访问#
- GC 使用自己的一套映射(通过第二次
mmap),该映射始终具有PROT_READ | PROT_WRITE权限。 - 因此,GC 可以随时扫描任何页,不受
mprotect限制。