【Linux】分页式存储管理:深刻理解页表映射

发布于:2025-05-29 ⋅ 阅读:(49) ⋅ 点赞:(0)

📝前言:

这篇文章我们来讲讲Linux——分布式存储管理

🎬个人简介:努力学习ing
📋个人专栏:Linux
🎀CSDN主页 愚润求学
🌄其他专栏:C++学习笔记C语言入门基础python入门基础C++刷题专栏


一, 物理内存管理

我们之前谈磁盘的时候说数据是按块(4KB)存储的,实际上内存也是会按4KB大小来划分的。(物理内存和磁盘之间,以4KB为单位交换)
4GB 的空间就是4GB/4KB = 1048576 个块,我们把这个块称为:页框page(一个页框对应到虚拟地址里称为页)
所有的页框,也需要描述 + 组织,在内核中用结构体struct page

/* include/linux/mm_types.h */
struct page
{
    /* 原⼦标志,有些情况下会异步更新 */
    unsigned long flags;
    union
    {
        struct
        {
            struct list_head lru;
            struct address_space *mapping;
            /* 在映射内的偏移量 */
            pgoff_t index;
            unsigned long private;
        };
        struct
        { /* slab, slob and slub */
            union
            {
                struct list_head slab_list; /* uses lru */
                struct
                { /* Partial pages */
                    struct page *next;
#ifdef CONFIG_64BIT
                    int pages;    /* Nr of pages left */
                    int pobjects; /* Approximate count */
#else
                    short int pages;
                    short int pobjects;
#endif
                };
            };
            struct kmem_cache *slab_cache; /* not slob */
            /* Double-word boundary */
            void *freelist; /* first free object */
            union
            {
                void *s_mem;            /* slab: first object */
                unsigned long counters; /* SLUB */
                struct
                {                        /* SLUB */
                    unsigned inuse : 16; 
                    unsigned objects : 15;
                    unsigned frozen : 1;
                };
            };
        };
        ...
    };
    union
    {
        atomic_t _mapcount;
        unsigned int page_type;
        unsigned int active; /* SLAB */
        int units;           /* SLOB */
    };
    ...
#if defined(WANT_PAGE_VIRTUAL)
        void *virtual;
#endif /* WANT_PAGE_VIRTUAL */
    ...
}
  • flags:用来存放页框的状态。这些状态包括页框是不是脏的,是不是被锁定在内存中等。flag的每⼀位单独表⽰⼀种状态,所以它⾄少可以同时表示出32种不同的状态
  • _mapcount :表示在页表中有多少项指向该页框,也就是这⼀页框被引用了多少次。当计数值变为-1时,就说明当前内核并没有引用这⼀页框,于是在新的分配中就可以使用它
  • virtual :是页框的虚拟地址。通常情况下,它就是页框在虚拟内存中的地址

所有的Page通过一个全局的 struct page 数组来管理,每个下标就对应一个Page

二,两级页表的地址转换

1. 页表

实际上,页表中并不是存储每个字节的地址,而页(Page)级别的映射。即:存储每个页框的起始虚拟地址,然后映射到页框的起始物理地址

如果整个物理内存映射到整个虚拟空间上,则如下如:
在这里插入图片描述
4GB/4KB = 1048576,所以页表中只需要存储2^20个地址。
但是,如果每个页表都这么大,还是太多了,于是就有了利用页目录结构的分级页表。

2. 页目录结构

把拥有2^20个项的页表再按2^10为一块划分,分成2^102^10块,于是第一个2^10成了页目录,第二个2^10才对应到真正的页表。

在这里插入图片描述

这就是二级页表结构,32位机器就常用这种划分方法

  • 31- 22位表示页目录的下标,第21 - 12位表示页表内的下标
  • 最后11- 0位(低12位)则是具体的地址在页框内的偏移量
  • 所以我们通过虚拟地址映射到页框的起始物理地址,然后起始物理地址 + 偏移量,就可以定位到某个字节的地址

则,在这种结构下:

  • 每个进程有自己的页目录(PDE 表,一级表),存储在内存中,并由 CR3 寄存器(x86)指向其物理地址。
  • 页表(PTE 表,二级表) 按需分配,只有当进程实际使用某块虚拟地址时,才会分配对应的页表。
    • 如果某块 4MB 的虚拟地址范围未使用,对应的页表可以不分配(只需在页目录中标记 P=0),而不是占用完整的 2^20 个 PTE。

3. 地址转换过程

假设我们现在拿到了一个虚拟地址,转换流程:

  1. 从 CR3 寄存器获取页目录的物理地址(CR3 存储当前进程的页目录基址)。
  2. 虚拟地址的 31-22 位作为页目录索引,找到对应的 PDE。
    • 检查 PDE 的 P 位,如果为 0,触发 缺页异常(Page Fault)。
  3. 从 PDE 中取出页表的物理地址。
  4. 虚拟地址的 21-12 位作为页表索引,找到对应的 PTE。
    • 检查 PTE 的 P 位,如果为 0,触发 缺页异常。
  5. 从 PTE 中取出物理页框号(PFN)(页框的物理起始地址)。
  6. 物理地址 = (PFN << 12) | 页内偏移(低 12 位)

以上工作都是由MMU(内存管理单元)硬件完成

但是,每次MMU都要先进行两次页表查询确定物理地址还是太慢了,于是就有了快表TLB(缓存)

【我们可以通过物理地址来找到对应的虚拟地址,有对应的宏可以帮助我们实现,原理也很简单,只需要针对性的拿地址中的数字,反映的就是下标】

4. TLB

  • 当CPU给MMU发了一个新的虚拟地址以后,MMU先去TLB里面找有没有
  • 如果没有,就再进行两次页表查询,找到了以后吧地址返回给CPU,并且把这条映射关系给到TLB,让它记录一下
    在这里插入图片描述

三. 缺页异常

当CPU 给 MMU 的虚拟地址,在 TLB 和页表都没有找到对应的物理页时,会引发缺页异常 Page Fault,它是⼀个由硬件中断触发的可以由软件逻辑纠正的错误。
在这里插入图片描述
缺页中断会交给 PageFaultHandler 处理,其根据缺页中断的不同类型会进行不同的处理:

  • Hard Page Fault(硬缺页错误/主要缺页错误):这时物理内存中没有对应的物理页,需要CPU打开磁盘设备读取到物理内存中,再让MMU建⽴虚拟地址和物理地址的映射。
  • Soft Page Fault(软缺页错误/次要缺页错误):这时物理内存中是存在对应物理页的,只不过可能是其他进程调入的(比如动态库),发出缺页异常的进程不知道而已,此时MMU只需要建立映射即可。
  • Invalid Page Fault(无效缺页错误) :如,内存地址越界访问,对空指针解引用等,内核就会报 segment fault 错误,中断进程直接挂掉

其他问题理解

1. 理解malloc和new

  • 实际上mallocnew开空间并没有开实际的物理空间,而是只分配了进程地址空间。(当虚拟空间分配好以后,OS就认为这是合法的,属于该进程的空间了,用mm_struct记录)
  • 当真正要使用的时候,会触发中断错误,CPU去执行中断处理方法,然后真正分配空间,建立映射
    • 分配内存本质上是:找到空闲的物理页框,并填充到页表中
  • 这是一种内存的延迟申请,可以实现把"暂时没用到"的物理内存先给别人用

所以我们申请内存,实际上只是申请的进程地址空间

2. 理解写时拷贝

  • 子进程和父进程指向的其实是同一个页框,权限管理以页框为单位
  • 当一个进程写入时,发现权限不对,触发缺页
  • 然后,CPU就会去中断向量表执行中断处理方法,对整个页框写时拷贝

🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!


网站公告

今日签到

点亮在社区的每一天
去签到