60 lines
3.5 KiB
Markdown
60 lines
3.5 KiB
Markdown
既然你已经理解了 `task_struct` 到 `mm_struct` 再到 `vm_area_struct`(VMA)这一套“账本”系统,那么**页表项(Page Table Entry, PTE)**就是这个系统中最后也是最关键的“映射条目”。
|
||
|
||
简单来说,VMA 只是在内核里划地盘(虚拟地址范围),而 **PTE 才是真正把虚拟内存指向物理内存的“指路牌”**。
|
||
|
||
在 x86-64 架构下,一个标准的页表项通常是 **64 位(8 字节)**。我们可以把它拆解为两个核心部分:**物理页框号(PFN)**和**标志位(Flags)**。
|
||
|
||
---
|
||
|
||
### 1. 页表项的逻辑构成
|
||
|
||
|
||
|
||
一个 PTE 的 64 位并不是乱排的,它的结构大致如下:
|
||
|
||
| 位范围 (Bits) | 名称 | 作用 |
|
||
| :--- | :--- | :--- |
|
||
| **0** | **P (Present)** | **存在位**。1 表示在物理内存中,0 表示不在(可能在 Swap 分区或未分配)。 |
|
||
| **1** | **R/W (Read/Write)** | **读写位**。0 表示只读,1 表示可读写。 |
|
||
| **2** | **U/S (User/Supervisor)** | **权限位**。0 表示仅内核可访问,1 表示用户态也可访问。 |
|
||
| **3** | **PWT / PCD** | 缓存策略控制(通常由驱动或内核底层管理)。 |
|
||
| **5** | **A (Accessed)** | **访问位**。只要该页被读或写过,硬件会自动置 1。这是 LRU 换出算法的重要依据。 |
|
||
| **6** | **D (Dirty)** | **脏位**。页面被写入过则置 1。如果是脏页,换出时必须写回磁盘。 |
|
||
| **7** | **PS (Page Size)** | 决定这是指向 4KB 页还是 2MB 大页。 |
|
||
| **12 ~ 51** | **Physical Address** | **物理地址(PFN)**。这是最核心的部分,存放 4KB 对齐的物理内存基地址。 |
|
||
| **63** | **NX (No-Execute)** | **禁止执行位**。如果置 1,该页的数据不能作为指令执行(防范缓冲区溢出攻击)。 |
|
||
|
||
---
|
||
|
||
### 2. 为什么地址位只有 12-51 位?
|
||
|
||
你可能会好奇,为什么物理地址不占满整个 64 位?
|
||
|
||
* **低 12 位(0-11)**:由于内存页通常是 4KB 对齐的($2^{12} = 4096$),物理地址的低 12 位永远是 0。所以这 12 位被挪作他用,变成了上面的 `P`, `R/W`, `A`, `D` 等**状态标志位**。
|
||
* **高位**:目前的处理器通常只支持 48 位或 52 位的物理寻址,所以 52 位之后的部分目前是保留或用于特殊属性(如 NX 位)。
|
||
|
||
---
|
||
|
||
### 3. 与 VMA 的协作关系
|
||
|
||
这是一个非常容易混淆的点:**VMA 里有权限,PTE 里也有权限,听谁的?**
|
||
|
||
答案是:**两者共同作用,取交集。**
|
||
|
||
1. **第一关 (软件检查)**:当你访问一个地址,内核先去红黑树找 VMA。如果 VMA 标记这块地只能读,而你想写,内核直接发 `SIGSEGV`(段错误)。
|
||
2. **第二关 (硬件检查)**:如果 VMA 允许,但 PTE 里的 `R/W` 位是 0(比如因为“写时复制” Copy-on-Write 机制),CPU 硬件会触发**缺页中断**,让内核来处理。
|
||
|
||
---
|
||
|
||
### 4. 深度思考:脏位 (Dirty Bit) 的意义
|
||
|
||
对于你这种偏向底层研究的开发者来说,`Dirty Bit` 非常重要:
|
||
* 如果一个物理页是“干净”的(和磁盘内容一致),内存紧张时可以直接丢弃。
|
||
* 如果 `Dirty Bit` 是 1,说明数据被改过了,内核必须先把它同步回磁盘(Swap 或文件系统),才能回收这个页。
|
||
|
||
---
|
||
|
||
### 接下来你可能感兴趣的:
|
||
既然页表项这么精细,但在 64 位系统下,如果每个进程都搞一个平铺的页表,会占用巨大的内存(几个 GB 甚至更多)。
|
||
|
||
**你想了解一下 Linux 是如何通过“多级页表(Multi-level Page Tables)”来节省这些空间的吗?** |