Linux页表的概念

目录

[1 物理内存与磁盘](#1 物理内存与磁盘)

[2 虚拟地址转化物理地址](#2 虚拟地址转化物理地址)

[2.1 页表](#2.1 页表)

[2.2 页表目录结构](#2.2 页表目录结构)

[2.3 两级页表的地址转换](#2.3 两级页表的地址转换)

[2.4 缺页异常](#2.4 缺页异常)


1 物理内存与磁盘


首先这里需要知道内存与磁盘在进行IO操作时都是以4KB大小为单位进行数据IO的

⼀个32位机器的可⽤的物理内存有4GB的空间,在逻辑上我们可以把物理内存按照4KB⼀个的固定⻓度的页框进⾏分割,每个⻚框包含⼀个物理页(page)。⼀个⻚的⼤⼩等于⻚框的⼤⼩

  • ⻚框是⼀个存储区域;
  • ⽽⻚是⼀个数据块,可以存放在任何⻚框或磁盘中;

按照⼀个⻚框的大小4KB 进⾏划分, 4GB 的空间就是 4GB / 4KB = 1048576 个⻚框

有这么多的物理⻚,操作系统肯定是要将其管理起来的,操作系统需要知道哪些⻚正在被使⽤,哪些⻚空闲等等

(1)struct page

struct page是Linux内核中一个非常重要的数据结构,用于描述和管理物理内存页。它是内核内存管理子系统的核心组成部分之一

cpp 复制代码
struct page {
    unsigned long flags;            /* 表示页的状态和属性 */
    union {
        struct {
            struct list_head lru;    /* 用于页面回收的LRU链表 */
            struct address_space *mapping; /* 如果页是文件缓存,指向所属的address_space */
            pgoff_t index;          /* 在映射中的偏移量 */
            unsigned long private;  /* 私有数据,具体含义取决于页的用途 */
        };
        struct {
            dma_addr_t dma_addr;    /* DMA使用的地址 */
        };
        /* 其他联合体成员... */
    };
    atomic_t _refcount;             /* 引用计数 */
    unsigned long counters;         /* 混合计数器 */
#if defined(WANT_PAGE_VIRTUAL)
    /* 内核虚拟地址(如果没有映射则为NULL,即⾼端内存) */
    void* virtual;
#endif /* WANT_PAGE_VIRTUAL *

    /* ... 其他成员 ... */
};

(2)主要成员说明

  • flags:
  1. 表示页的状态和属性,使用位掩码
  2. 常用标志包括:PG_locked, PG_error, PG_referenced, PG_dirty等
  3. 定义在
  • _mapcont:
  1. 表⽰在⻚表中有多少项指向该⻚,也就是这⼀⻚被引⽤了多少次
  2. 当计数值变为-1时,就说明当前内核并没有引⽤这⼀⻚,于是在新的分配中就可以使⽤它
  • mapping:
  1. 如果页是文件缓存页,指向所属的address_space
  2. 如果页是匿名页,最低位用于特殊标记
  • index:
  1. 在映射中的偏移量(以页为单位)
  2. 对于文件页,表示在文件中的位置
  3. 对于匿名页,表示在vma中的位置
  • _refcount:
  1. 页的引用计数,表示有多少实体正在使用该页
  2. 当_refcount为0时,页可以被释放
  • lru:
  1. 用于页面回收的LRU链表
  2. 内核使用它来跟踪最近最少使用的页
  • virtual:
  1. ⻚的虚拟地址。通常情况下,它就是⻚在虚拟内存中的地址

2 虚拟地址转化物理地址


2.1 页表

⻚表中的每⼀个表项,指向⼀个物理⻚的开始地址

在 32 位系统中,虚拟内存的最⼤空间是 4GB ,这是每⼀个⽤⼾程序都拥有的虚拟内存空间。既然需要让 4GB 的虚拟内存全部可⽤,那么⻚表中就需要能够表⽰这所有的 4GB 空间,那么就⼀共需要 4GB/4KB = 1048576 个表项

虚拟内存看上去被线"分割"成⼀个个单元,其实并不是真的分割,虚拟内存仍然是连续的。这个线的单元仅仅表⽰它与⻚表中每⼀个表项的映射关系,并最终映射到相同⼤⼩的⼀个物理内存⻚上。

⻚表中的物理地址,与物理内存之间,是随机的映射关系,哪⾥可⽤就指向哪⾥(物理⻚)。虽然最终使⽤的物理内存是离散的,但是与虚拟内存对应的线性地址是连续的。处理器在访问数据、获取指令时,使⽤的都是线性地址,只要它是连续的就可以了,最终都能够通过⻚表找到实际的物理地址。

在 32 位系统中,地址的⻓度是 4 个字节,那么⻚表中的每⼀个表项就是占⽤ 4 个字节。所以⻚表占据的总空间⼤⼩就是: 1048576*4 = 4MB 的⼤⼩。也就是说映射页表⾃⼰本⾝,就要占⽤ 4MB /4KB = 1024 个物理⻚。这会存在哪些问题呢?

当初为什么使⽤⻚表,就是要将进程划分为⼀个个⻚可以不⽤连续的存放在物理内存中,但是此时⻚表就需要1024个连续的⻚框,似乎和当时的⽬标有点背道⽽驰了

此外,根据局部性原理可知,很多时候进程在⼀段时间内只需要访问某⼏个⻚就可以正常运⾏了。因此也没有必要⼀次让所有的物理⻚都常驻内存。

为了解决这个问题,可以把这个单⼀⻚表拆分成 1024 个体积更⼩的映射表。如下图所⽰。这样⼀来,1024(每个表中的表项个数) * 1024(表的个数),仍然可以覆盖 4GB 的物理内存空间

从总数上看是这样,但是⼀个应⽤程序是不可能完全使⽤全部的 4GB 空间的,也许只要⼏⼗个⻚表就可以了

例如:⼀个用户程序的代码段、数据段、栈段,⼀共就需要 10 MB 的空间,那么使⽤ 3 个⻚表就⾜够了

2.2 页表目录结构

到⽬前为⽌,每⼀个⻚框都被⼀个⻚表中的⼀个表项来指向了,那么这 1024 个⻚表也需要被管理起来。管理⻚表的表称之为⻚⽬录表,形成⼆级⻚表。

  • 所有⻚表的物理地址被⻚⽬录表项指向
  • ⻚⽬录的物理地址被 CR3 寄存器 指向,这个寄存器中,保存了当前正在执⾏任务的⻚⽬录地址。

所以操作系统在加载⽤⼾程序的时候,不仅仅需要为程序内容来分配物理内存,还需要为**⽤来保存程序的⻚⽬录和⻚表分配物理内存**

2.3 两级页表的地址转换

在32位机器上,一个虚拟地址占32个比特位,在虚拟地址转换为物理地址的过程中虚拟地址不是一个整体,而是会被分为三部分:

虚拟地址中低 12 位为⻚偏移,剩下⾼ 20 位给⻚表,分成两级,每个级别占 10 个bit(10+10)

将虚拟地址( 0x 0000000000,0000000001,11111111111 )转换为物理地址的过程:

总结:

单级⻚表对连续内存要求⾼,于是引⼊了多级⻚表,但是多级⻚表也是⼀把双刃剑,在减少连续存储要求且减少存储空间的同时降低了查询效率
细节:

  1. CR3保存的时当前进程的页表的物理地址
  2. 编译器不参与虚拟地址的划分
  3. 高20位相同的地址,一定是存放在同一个页框的,相同属性的代码或者数据,在同一个4KB的数据块中
  4. 虚拟地址与物理地址是可以进行相互转换的
  5. 进程首次加载时,操作系统会申请内存,创建page,填充页框的物理地址和页表的物理地址
  6. 所有变量都只有一个地址,第一个字节的地址,因此类型决定的了最终访问多少字节
  7. 操作系统内,申请和管理物理内存都是以4KB为单位的,因此在进行写时拷贝或者缺页中断时,都是以4KB为单位进行的
  8. C/C++在语言层面又属于自己的内存管理机制,类似于STL库中的空间配置器。在语言层面会预先申请数据块,在需要的时候会从预先申请的数据中分配资源
  9. 进程页表的本质是进程看到资源的"窗口",因此谁拥有更多的地址,谁就拥有更多的物理内存资源

2.4 缺页异常

设想,CPU 给 MMU 的虚拟地址,在 TLB 和⻚表都没有找到对应的物理⻚,该怎么办呢?

其实这就是缺⻚异常 Page Fault ,它是⼀个由硬件中断触发的可以由软件逻辑纠正的错误

假如⽬标内存⻚在物理内存中没有对应的物理⻚或者存在但⽆对应权限,CPU 就⽆法获取数据,这种情况下CPU就会报告⼀个缺⻚错误。

由于 CPU 没有数据就⽆法进⾏计算,CPU罢⼯了⽤⼾进程也就出现了缺⻚中断,进程会从⽤⼾态切换到内核态,并将缺⻚中断交给内核的 Page Fault Handler 处理。


缺⻚中断会交给 PageFaultHandler 处理,其根据缺⻚中断的不同类型会进⾏不同的处理:

  1. Hard Page Fault 也被称为 Major Page Fault ,翻译为硬缺⻚错误/主要缺⻚错误,这时物理内存中没有对应的物理⻚,需要CPU打开磁盘设备读取到物理内存中,再让MMU建⽴虚拟地址和物理地址的映射。
  2. Soft Page Fault 也被称为 Minor Page Fault ,翻译为软缺⻚错误/次要缺⻚错误,这时物理内存中是存在对应物理⻚的,只不过可能是其他进程调⼊的,发出缺⻚异常的进程不知道⽽已,此时MMU只需要建⽴映射即可,⽆需从磁盘读取写⼊内存,⼀般出现在多进程共享内存区域。
  3. Invalid Page Fault 翻译为⽆效缺⻚错误,⽐如进程访问的内存地址越界访问,⼜⽐如对空指针解引⽤内核就会报 segment fault 错误中断进程直接挂掉。
相关推荐
ShineWinsu2 小时前
对于Linux:环境变量的解析
linux·面试·笔试·进程·环境变量·本地变量·getenv
坚持就完事了2 小时前
Linux上编写和运行Python\Java
linux·运维·服务器
wal13145202 小时前
OpenClaw 2026.4.5:视频/音乐生成内置,11 种语言支持,多个安全修复
运维·服务器·人工智能·安全·openclaw
超绝振刀怪2 小时前
【Linux 环境变量和地址空间】
linux·环境变量·fork·写诗拷贝
OPHKVPS2 小时前
Anthropic官方Git MCP服务器曝三重漏洞:提示注入即可实现文件读写与远程代码执行
运维·服务器·git
正经教主2 小时前
【docker基础】第三课:镜像管理与Dockerfile基础
运维·docker·容器
老虎06272 小时前
Nginx
运维·nginx
lwx9148529 小时前
Linux-特殊权限SUID,SGID,SBIT
linux·运维·服务器
皮卡狮10 小时前
Linux权限的概念
linux