图片无法显示,请右键点击新窗口打开图片

BiscuitOS 内存管理之分页大专题订阅入口

目录
  • 分页大专题导论

  • 分页大专题学习方法论

  • 内存流动

  • 专题视频合集

  • 虚拟内存大专题

  • 物理内存大专题

  • 页表大专题

  • 缺页大专题

  • TLB 大专题

  • GUP 大专题

  • 工程落地

    • 集中页表分配项目

    • 用户空间分配器项目

    • 内核空间分配器项目

图片无法显示,请右键点击新窗口打开图片


分页大专题导论

内存管理作为 Linux 四大子系统之一,其具有应用面广、场景面负责、代码逻辑复杂的特点,是开发者难以跨越的一座大山。BiscuitOS 社区计划推出 10 个实践大专题,通过内存流动内存场景两个手段,向广大开发者提供一套逻辑完整、强实践、强实用、强图形化的实践专题来学习 Linux 内存管理. BiscuitOS 分页大专题作为第一个大专题, 从内存管理的基础核心入手,带各位开发者入门 Linux 内存管理. 本文就带各位开发者一起了解什么是分页大专题.

图片无法显示,请右键点击新窗口打开图片

在很早的机器上,CPU 可以直接访问物理内存,此时 CPU 访问的地址空间等价于物理地址空间,这个时代的机器在运行多个任务的时候,多个任务分区域使用有限的物理内存,这样就导致系统不可能运行太多的任务,以及每个任务能够使用的内存相当有限. 随着技术的不断革新引入了分页机制,CPU 不再直接访问物理内存,取而代之的虚拟内存,虚拟内存的好处就是每个进程都可以分配一个独立的地址空间,在这个地址空间里只有进程自己和内核,另外只有 CPU 真正去访问虚拟内存时,虚拟内存才会建立页表映射到物理内存上,这样即增加了进程之间的资源隔离性和安全性,又将有限的物理内存资源无限的放大.

图片无法显示,请右键点击新窗口打开图片

由于分页机制的引入,带来了一堆新的硬件和软件层面的大变革,这也导致内存管理发展越来越复杂. 当无论任何一个复杂的事物都是有最简单和最基础的基础单元构成的,分页机制之后的内存管理也不例外,开发者只有了解了基础单元之后才有可能窥探整个内存管理子系统. 本文就从内存管理的基础单元为引子带大家慢慢揭开内存管理神秘面纱. 分页机制引入之后出现三个最基本的概念: 虚拟内存页表物理内存,三者的关系是: 虚拟内存只有通过页表映射到物理内存,那么 CPU 才能正常访问内存. 作为入门者可以这么理解三者:

  • 虚拟内存: 分页模式开启之后,CPU 直接访问的是虚拟地址,CPU 可寻址的空间称为虚拟地址空间,其被划分两部分: 用户空间和内核空间. 每个进程都有一个独立的虚拟地址空间,进程只能看到自己和内核独占使用整个虚拟空间,因此形成了每个进程都能一块独立且连续的虚拟地址空间.
  • 物理地址: 无论分页模式是否开启,CPU 物理地址总线能够寻址的空间称为系统物理地址空间,系统物理地址空间上分布值物理内存、系统预留物理内存和外设寄存器映射到系统物理地址空间形成的 MMIO
  • 页表: 分页模式开启之后,系统会建立虚拟内存到物理内存的映射逻辑,当 CPU 访问虚拟内存时,硬件会根据这个映射逻辑找到最终的物理内存,然后 CPU 才能正常访问这块内存.

通过三个基础单元,Linux 内核构建了复杂而又灵活的内存管理子系统,满足千千万万的内存需求,让有限的内存资源等到最大限度的使用,也为程序带来更加高效的内存使用. 因此想要把内存管理子系统学好,一定要围绕这三个基本单元去学习.

图片无法显示,请右键点击新窗口打开图片

要了解虚拟内存,其实只要能回答两个问题就够: 谁负责分配,分配之后属于什么内存. 在上图中我将虚拟内存按经典的用户空间和内核空间进行划分,因此在在讨论这两个问题时也分开讨论. 首先对于用户空间这两个问题的回答如下:

  • 谁负责分配: 用户空间通过 mmap/malloc/brk/sbrk 等接口为应用程序分配虚拟内存,并提供了三种分配方式:
    • PreAlloc(预分配): 在为进程分配虚拟内存的同时也把物理内存分配好,并建立了虚拟内存到物理内存的映射,这样的优点是进程能够快速访问分配好的虚拟内存,缺点如果不能有效的利用虚拟内存那么就是浪费物理内存.
    • LazyAlloc(惰性分配): 在为进程分配内存时只分配虚拟内存,只有当进程访问虚拟内存时才去真正的分配物理内存,并建立页表映射, 这样的优点是需要多少内存就分配多少内存,缺点也很明显对于首次访问的内存会很慢.
    • OnDemand(按需分配): 在为进程分配内存时只分配虚拟内存,进程可以在访问虚拟内存之前,告诉内核向内存里提前写入预设的数据,那么进程可以访问到预设的数据.
  • 分配之后属于什么内存: 分配之后内存的类型决定其用于什么目的,用户空间存在如下类型的内存:
    • 匿名内存(Anonymous Memory): 没有映射后端磁盘文件的内存,用于用户进程的堆、栈和任务的内存, 由于没有后端文件,因此该类型内存可以被压缩或者被换出
    • 共享内存(Shared Memory): 多个进程可以共享的内存, 用于文件共享和进程间通信. 同样共享内存没有后端文件,因此可以被压缩或者被换出
    • 文件映射内存(File-Mapping Memory): 将文件映射到进程的地址空间,像普通内存一样访问文件,该内存就是常说的 PAGE-CACHE
    • PFN-Mapping Memory: 映射到系统预留内存的虚拟内存,进程可以独立内存管理的内存,满足进程特殊内存需求
    • THP(透明大页内存): 采用大页粒度映射的虚拟内存,可以是匿名内存、文件映射内存也可以是共享内存,减少 TLB 刷新
    • HugeTLB 内存: 专门为用户空间提供的大页内存,有利于节省页表和减少 TLB 刷新.
    • DAX-Mapping Memory: 采用 DAX 方式映射的的内存,可以让用户空间可以直接使用 PMEM 内存
    • MMIO-Mapping Memory: 让虚拟内存映射到 MMIO 上,可以让用户进程直接访问 MMIO

回到了这两个问题之后,可以知道用户空间的虚拟地址可以通过这三种方式进行分配,分配之后可以形成多种类型的内存,每种类型的内存满足不同的需求场景。那么开发者在今后用户空间的使用过程中就知道什么时候用什么内存。那么接下来来看看内核空间的虚拟内存. 内核空间的虚拟内存被换分成不同的功能区域,每个区域都用相应的分配器负责虚拟内存的分配,分配完之后就按虚拟区域的功能使用,其也可以回答两个问题,具体如下:

  • 线性映射区: 由 Buddy 分配器进行管理并负责分配,Buddy 分配器分配的是物理内存,但可以通过简单的线性公式就可以获得对应的虚拟地址,线性映射区的内存主要满足内核线程日常的内存需求, 其内存特点是虚拟内存连续且映射的物理内存也连续.
  • VMALLOC 区域: 由 VMALLOC 分配器进行管理,并负责虚拟内存的分配和页表建立,VMALLOC 区域的内存主要是为内核提供虚拟连续而物理内存不连续的大块内存,以满足一些需要大内存但对物理地址连续性没有要求的场景.
  • TEMP-MAPPING 临时映射区: 由 KAMP/IOREMAP/MEMREMAP 分配器进行管理,并负责虚拟内存的分配和页表建立,用于内核需要将物理区域临时映射到内核空间,然后短暂使用,使用完毕之后立即释放的场景.
  • MMIO-MAPPING 映射区: 由 IOREMAP 分配器进行管理,并负责虚拟内存的分配和页表建立,用于将内核空间虚拟内存映射到 MMIO 上,然后让内核可以直接访问 MMIO
  • FIXMAP 固定映射区: 由 FIXMAP 分配器进行管理,并负责虚拟内存的分配和页表建立,固定映射区的虚拟内存是用于特定功能使用,因此具有固定的虚拟地址,而物理内存则是随机的. 因此该内存用于特定的内核功能.
  • Permanent-Mapping 区: 永久映射区是在内核编程阶段就决定虚拟地址的含义和用途,其从内核启动一直到内核关闭都保持虚拟内存指定的含义和用途.

图片无法显示,请右键点击新窗口打开图片

图片无法显示,请右键点击新窗口打开图片

BiscuitOS 内存管理之分页大专题订阅入口

图片无法显示,请右键点击新窗口打开图片