问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

【进程空间】通过页表寻址的过程

创作时间:
作者:
@小白创作中心

【进程空间】通过页表寻址的过程

引用
CSDN
1.
https://m.blog.csdn.net/qq_62987647/article/details/139272956

在现代操作系统中,虚拟内存管理是确保多个进程能够安全、高效地共享物理内存的关键机制。其中,页表作为虚拟地址到物理地址映射的核心数据结构,其设计和实现直接影响着系统的性能和资源利用率。本文将深入探讨32位操作系统中页表的结构和寻址过程,帮助读者理解这一基础但至关重要的技术细节。

页框、页表和页目录的概念

页框

页框(Page Frame)是物理内存中的固定大小的块,通常为4KB。在物理内存中,页框是用于存储实际数据的最小单元。假设内存大小为4GB,也就能划分出 1024*1024个 页框。 当进程通过页表来访问物理地址时,其本质就是找到页框的地址。


于是,操作系统对内存的管理,就可以看成是对页框即块的管理。那内核中又是如何描述一个页框的呢?
在Linux内核中,使用struct page结构来描述内存中的每一页框。这个结构体定义在内核头文件include/linux/mm_types.h中。该结构体通常有以下字段:

  • flags:标志位,表示页面的各种状态
  • mapping:指向该页框所属的地址空间
  • index:该页框在地址空间中的偏移量
  • _maocount:记录该页框到页表条目数
  • _refcount:页面引用计数。表示有多少用户正在使用该页框

页表

页表是用于存储物理内存与虚拟内存地址之间的映射关系的结构。每个进程都有属于自己的页表。页表中的每一个表项,都对应着一个页框的物理地址。一个页表项(PTE)通常包括以下几个字段:

  • 页框基地址:页框的第一个字节的地址
  • 控制位:包括存在位(P)、读/写位(R/W)、超级用户位(U/S)等。

给出32位页表项的控制位示例:

(上图来自知乎)

再给出内核中描述页表项的结构体代码,该结构体其实是一个位段,所有变量加起来一共32个bite位,即4字节:

// 页表项(PTE)的定义
typedef struct {
    uint32_t present    : 1;  // 存在位
    uint32_t rw         : 1;  // 读/写位
    uint32_t us         : 1;  // 用户/超级用户位
    uint32_t pwt        : 1;  // 页级写穿透位
    uint32_t pcd        : 1;  // 页级缓存禁用位
    uint32_t accessed   : 1;  // 访问位
    uint32_t dirty      : 1;  // 脏位
    uint32_t pat        : 1;  // 页属性表位
    uint32_t global     : 1;  // 全局位
    uint32_t available  : 3;  // 可用位
    uint32_t page_frame_base : 20; // 页框基地址
} __attribute__((packed)) page_table_entry_t;

页表其本质就是这种结构体数组。

页目录

页目录是用来管理页表的结构。页目录中每一表项都指着一张页表的物理地址。一个页目录项(PDE)通常包括以下几个字段:

  • 页表基地址:页表的首地址(物理)
  • 控制位:包括存在位(P)、读/写位(R/W)、超级用户位(U/S)等。

给出32位页目录项的控制位示例:

(上图来着知乎)
同样,给出内核中描述页目录项的代码:

// 页目录项(PDE)的定义
typedef struct {
    uint32_t present    : 1;  // 存在位
    uint32_t rw         : 1;  // 读/写位
    uint32_t us         : 1;  // 用户/超级用户位
    uint32_t pwt        : 1;  // 页级写穿透位
    uint32_t pcd        : 1;  // 页级缓存禁用位
    uint32_t accessed   : 1;  // 访问位
    uint32_t reserved   : 1;  // 保留位
    uint32_t ps         : 1;  // 页大小(通常为4KB页)
    uint32_t ignored    : 1;  // 忽略位
    uint32_t available  : 3;  // 可用位
    uint32_t page_table_base : 20; // 页表基地址
} __attribute__((packed)) page_directory_entry_t;

综上,我们知道了页框、页表、页目录的概念,以及彼此之间的联系。那么在一个进程建立的时候, 页表和页目录的内容是固定的吗? 答案是否定的。

页表和页目录的分配

在Linux内核中,新进程的页目录表的内容是动态增加的。这意味着页目录表和页表的分配更新会根据实际情况动态进行,而不是在创建进程时一次初始化所有的页表项。
大致步骤如下:

  1. 进程创建,建立并初始化新页目录表。该表大部分内容为空,有部分指向共享内核。
  2. 访问虚拟地址,如果该地址对应的页表项不存在,触发页故障。内核捕捉这个故障,然后给进程分配对于的页表,并更新页目录表页页表。

一级页表和二级页表

在操作系统中,分页机制可以采用单级页表和多级页表进行地址映射。Linux采用的就是多级页表。不同的分页机制会导致寻址的空间成本不同(页表本身也占空间)。下面以一级页表和二级页表为例,分析各自的空间成本。
单级页表:只有一张大的页表来映射虚拟空间和物理空间。结构较为简单,直接映射。
多级页表:分层次的管理地址空间,且一般都有一张页目录表。结构较为复杂,需要多次转换地址。

一级页表

总共就一张表。

  • 页表项大小:通常每一个页表项都是 4 个字节,即32个比特位。
  • 页表项数目:因为4GB内存一共有 1024*1024 个页框,要想映射所有页框。也就需要1024*1024个页表项。

一级页表的总大小:4bite10241024=4MB

当我们采用一级页表。给出一个虚拟地址(32位),如何找到目标的物理地址(某一字节)呢?

寻址过程

将虚拟地址分解为两个部分:

  1. 页表索引:高20位。因为一张页表的总项数为1024*1024=2^20(页框数),虚拟地址的高20位用来表示页表项的位置。
  2. 页内偏移量:低12位。通过高20位能找到一个唯一的页表项,即页框的物理地址。一个页框占4096个字节,用12位比特位恰好能表示4096个不同的位置。于是通过低12位比特位的组合,我们就能找到页框内的唯一一个字节。

假设现在有一个虚拟地址为:0x0000 1005取出高20位的十进制值为1,低12位的十进制值为5.于是在页表中找到下标为1的页表项,通过这个页表项我们能找到一个唯一的页框。再在该页框中从找到第5个字节的地址。该地址就是0x0000 1005对应的物理地址。

二级页表

在二级页表中,最外层的页表我们当成一个页目录。页目录中的每一项都指向一张页表。一共有1024个页目录项,即一共有1024张页表,每一张页表有1024个页表项,刚好对应1024*1024个页框。
1.页目录 大小计算:

页目录总大小 :4byte*1024=4KB

2.每个页表 的大小计算:

  • 页表项大小:4字节
  • 页表项数量:每一个页表都有1024个页表项

每个页表的大小:10244byte=4KB
页表的总大小:10244KB=4MB
二级页表的总大小:4MB+4KB约等于4MB

寻址过程

将虚拟地址分为三个部分:

  1. 页目录索引:高10位用来索引页目录项,10个二进制位最多表示1024个位置。
  2. 页表索引:中间10位索引页表项
  3. 页内偏移量:低12位用于表示页内偏移量,12个二进制位最多表示4096个位置,刚好对应页框内每个字节的相对位置。

当我们拿到一个虚拟地址,取出高10位的二进制表示的值,作为页目录的下标找到目标页表。再取出中间10位二进制表示的值,用来作为目标页表的下标,来找到目标页框。最后取出低12位,在目标页框中找到唯一一个字节地址。

假设现在有一个虚拟地址0x00001005,得到页目录索引为0,页表索引为1,页内偏移量为5.所以我们就去下标为0的页目录项种去找到页表,并在该页表下标为1的页表项中找到页框,最后在该页框中找到第5个字节的地址。

一级页表和二级页表的对比

尽管从理论上看,一级页表和二级页表的总空间成本差不多。但是二级页表的使用效率会更高。
如果采用一级页表,每个进程都需要分配整个页表的内存,也就意味着每个进程都需要有4MB的空间来存放页表,且页表项很多都是空的。这对大多数应用来说都是一种浪费。

如果采用二级页表或者三级页表。每个进程拥有一张页目录,页表的数量会根据实际情况来进行分配,因此实际内存小于4MB。这样一来,避免了为进程分配大量未使用的页表,从而节省了内存。
从时间效率上来看,虽然一级页表直接可以找到页框的地址,而多级页表需要经过多次索引,但是多级页表带来空间上的节省是值得多消耗一些索引时间的

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号