重要结构
mm_struct:描述一个进程的“整个用户虚拟地址空间”的内核抽象(全局视角)。vm_area_struct:描述该地址空间里的“一段连续的映射区域(VMA)”(局部视角)。mm_context_t(mm_context):与体系结构相关的、供硬件/MMU 使用的“地址空间硬件上下文”(架构视角),嵌在mm_struct里。
详情
mm_struct(进程地址空间描述符)
- 表示一个进程(或一组共享 mm 的线程)的整个用户虚拟地址空间。
- 关键内容:
- VMA 集合(老内核用红黑树,近年主线用 maple tree 保存/检索)。
- 顶级页表根(如
pgd/mm->pgd)。 - 统计信息(RSS、swap 计数等)、
mmap_lock等地址空间级锁。 - 引用计数(
mm_users/mm_count)、属主线程、内存上限等。 - 架构相关上下文
mm_struct::context(类型为mm_context_t)。
- 进程与线程关系:
- 普通线程共享同一个
mm_struct。 - 内核线程通常
tsk->mm == NULL,借用active_mm。
- 普通线程共享同一个
- 你可以把它理解为“这个进程的用户态内存长什么样、有哪些映射、页表在哪、怎么加锁管理”。
vm_area_struct(单个映射区域 VMA)
- 描述虚拟地址空间中一段“连续、同属性”的内存区域,如可执行段、堆、栈、匿名映射、文件映射的一段等。
- 关键字段(常见):
vm_start/vm_end:虚拟地址范围。vm_flags:权限与属性(如读/写/执行、私有/共享、是否可扩展等)。vm_file+vm_pgoff:若为文件映射,指向底层文件与偏移;匿名映射则没有文件。vm_ops:按需缺页处理等回调。
- 所有 VMA 共同组成
mm_struct的完整地址空间布局。缺页异常时,内核会在mm的 VMA 索引结构里查找对应的vma来决定权限与缺页处理。
mm_context_t / mm_context(架构相关的硬件上下文)
- 每个
mm_struct都有一个context字段,类型为mm_context_t(在各架构的arch/*/include/asm/mmu.h定义)。 - 存放“硬件/MMU 需要的、与地址空间绑定的状态”,高度依赖架构:
- 例:x86 可能包含 LDT、进程地址空间的 PCID/ASID、pkeys/PKRU 状态(见
arch/x86/include/asm/pkeys.h),以及 TLB 相关信息。 - 例:arm64 通常包含 ASID、TTBR0 等。
- 例:x86 可能包含 LDT、进程地址空间的 PCID/ASID、pkeys/PKRU 状态(见
- 作用是在任务切换或
switch_mm()时,把“这个mm的硬件态”正确装载到 CPU(如切换 CR3/TTBR、ASID、PKRU 等),并配合 TLB 管理。
三者关系与区别
- 层次关系
mm_struct:一整个地址空间的“软件描述”。vm_area_struct:该地址空间中的“一个片段”的软件描述。mm_context_t:这个地址空间对应的“硬件/MMU 状态”,是mm_struct的一部分,但由各架构定义与使用。
- 职责边界
mm_struct负责组织/管理 VMA 集合、页表根、统计与锁。vm_area_struct负责描述具体映射区的范围、权限、来源(匿名/文件)与缺页处理方式。mm_context_t负责硬件视角的上下文(ASID/PCID/PKRU/LDT…),保证在 CPU 上正确访问该地址空间并高效管理 TLB。
生命周期与并发要点
- 共享与引用
- 多线程共享同一个
mm_struct和其下所有vm_area_struct。 mm_context_t随mm_struct的创建/销毁而初始化/清理。
- 多线程共享同一个
- 锁
- 访问/修改 VMA 集合需使用
mm->mmap_lock(读锁查找、写锁修改)。 - 页表修改还会用到更细粒度的页表锁。
- 访问/修改 VMA 集合需使用
- 典型路径
- 缺页异常:找到
current->mm→find_vma()定位vma→ 检查vm_flags→ 走vm_ops->fault或通用缺页处理 → 建立页表项。
- 缺页异常:找到
MPK
- 在 x86 上,
mm_context_t会包含与内存保护键(pkeys/PKRU)相关的状态,定义见arch/x86/include/asm/pkeys.h,管理逻辑在arch/x86/mm/pkeys.c。 mm_struct的定义在include/linux/mm_types.h,其中的context字段就是mm_context_t。
mm_context_t中有两个字段:
/*
* One bit per protection key says whether userspace can
* use it or not. protected by mmap_lock.
*/
u16 pkey_allocation_map; //每个pkey是否被使用,用于alloc/free
s16 execute_only_pkey; //所有xom共用一个pkey内存保护相关系统调用
mmap、mprotect会尝试分配pk来实现xom(当prot只有PROT_EXEC)时。如果pk已经被耗尽了,则对应内存仍可读取,且不会产生任何错误信息。
手动pk分配
使用pkey_alloc来分配并设置一个pk的值,pkey_free来释放。
pkey_mprotect来为某段内存指定pkey编号。
内核中的相关接口实现在mm/mprotect.c
static int do_mprotect_pkey(unsigned long start, size_t len, unsigned long prot, int pkey);
SYSCALL_DEFINE4(pkey_mprotect, unsigned long, start, size_t, len, unsigned long, prot, int, pkey);
SYSCALL_DEFINE2(pkey_alloc, unsigned long, flags, unsigned long, init_val);
SYSCALL_DEFINE1(pkey_free, int, pkey);