在 Linux 里,不同的场景和不同的内存行为都会导致缺页,缺页异常处理函数为了更好的处理缺页,因此需要获得引起缺页的原因。不同的架构有特定的寄存器在发生缺页异常时,将缺页原因写入指定寄存器,那么缺页异常处理函数可以从该寄存器中获得异常原因,进而更准确的处理缺页. 在 Intel X86 架构上发生缺页(页面故障)的原因包括以上几种,硬件上使用一个 32 位寄存器进行维护,每个 Bit 的含义如下:
- BIT0 PRESENT: 置位时表示因为违法页级权限保护,例如进程没有满足 Protection Key 设定的权限导致缺页; 清零时表示因为虚拟内存不在系 统物理地址空间,这里分为两种情况,第一种情况是还没有为虚拟内存分配对应的物理内存引起的 #PF, 另外一种情况是已经为虚拟内存分配对应的物 理内存,只是物理内存被交换到 SWAP Space 上.
- BIT1 Write/Read: 置位时表示读操作引起的缺页,例如发生缺页时进程正在读虚拟内存; 清零时表示写操作引起的缺页,例如发生缺页时正在>写虚拟内核,或者对写保护的虚拟内存执行写操作.
- BIT2 User/Super: 置位表示用户模式访问引起的缺页; 清零表示内核模式访问引起的缺页.
- BIT3 RSVD: 置位表示页表中 RSVD 的标志位被置位引起的.
- BIT4 INSTR: 置位表示因为访问的指令对应的物理内存不存在引起的缺页.
- BIT5 PK: 置位表示违法了 Protection Key 制定的权限引起的缺页
- BIT6 SS: 置位表示访问 Shadow-Stack 引起的缺页
- BIT15 SGX: 违法了 SGX 制定的权限引起的缺页
原理了解差不多,如果从内存场景角度来看,这些 ERROR Code 是如何产生的,那么接下来通过实践案例了解每种 ERROR CODE 产生的场景:
ERROR CODE: PF_PRESENT
PF_PRESENT 物理内存未分配场景
PF_PRESENT 物理内存 SWAP OUT 不在系统物理地址空间场景
PF_PRESENT 物理内存被内存压缩不在系统物理地址空间场景
ERROR CODE: PF_READ
ERROR CODE: PF_WRITE
PF_WRITE 首次写操作触发异常场景
PF_WRITE 对写保护的内存执行写操作场景
ERROR CODE: PF_USER
PF_USER 用户进程访问 Userspace 虚拟地址
PF_USER 用户进程访问 Kernep Space 虚拟地址
ERROR CODE: PF_SUPER
PF_SUPER 内核线程访问 Kernel Space 虚拟地址场景
PF_SUPER 内核线程访问 Userspace 虚拟地址场景
ERROR CODE: PF_INSTR
ERROR CODE: PF_PK
ERROR CODE: PF_PROT
PF_PROT: mprotect 动态修改权限导致非法访问场景
PF_PROT: Protection Key 动态修改权限导致非法访问场景
ERROR CODE: PF_RSVD