当前位置: 首页 > news >正文

Linux内核引导内存分配器原理

一、bootmem分配器

        在内核初始化的过程中需要分配内存,内核提供临时的引导内存分配器,在页分配器和块分配器初始化完成之后,把空闲的物理页交给页分配器管理,丢弃引导内存分配器。bootmem 分配器定义的数据结构,内核源码如下:

         每个内存节点后一个bootmem_data实例:

         bootmem分配器的算法:      

                a. 只把低端内存添加到 bootmem 分配器,低端内存是可以直接映射到内核虚拟地址空间的物理内存;
                b. 使用一个位图记录哪些物理面被分配,如果物理页被分配,把这个物理页对应的位设置为 1;
                c. 采用最先适配算法,扫描位图,找到第一个足够大的空闲内存块;
                d. 为了支持分配小于一页的内存块,记录上次分配的内存块的结束位置后面一个字节的偏移和后面一页的索引,下次分配时,从上次分配的位置后面开始搜索。如果上次分配的最后一个物理页剩余空间足够,可以直接在这个物理页上分配内存。

        bootmem 分配器对外提供分配内存函数 alloc_bootmem,释放内存的函数是 free_bootmem。ARM64 架构内核不使用 bootmem 分配器,但是其他处理器架构还在使用 bootmem 分配器。

二、memblock分配器

1.数据结构     

        物理内存类型和内存类型区别:内存类型是物理内存类型的子集,在引导内核时可以使用内核参数 "mem=nn [KMG]",指定可用内存的大小,导致内核不能看见所有的内存;物理内存类型总是包含所有内存范围,内存类型只包含内核参数 "mem=" 指定的可用内存范围。

举例解释:

  • 物理内存类型:代表硬件实际存在的所有内存。例如,计算机实际安装了 16GB 内存,无论内核如何配置,物理内存类型始终涵盖这 16GB 的全部范围。
  • 内存类型:受内核参数控制的可用内存范围。若引导内核时使用参数 mem=10G,内核会将可用内存限制为 10GB(即只管理这 10GB),此时内存类型仅包含这 10GB,是物理内存类型(16GB)的子集。这说明通过 mem 参数,内核主动 “忽略” 部分物理内存,仅管理指定范围内的内存。

2.内存块类型的数据结构

3.memblock、memblock_type、memblock_region区别

1. 三个数据结构的作用关系

(1)memblock_region

  • 作用:描述单个物理内存区域的详细信息。

    • 字段

      • base:起始物理地址。

      • size:区域大小。

      • flags:标志位(如 MEMBLOCK_NOMAP)。

      • nid(可选):NUMA 节点编号。

  • 示例
    一个内存区域 0x1000-0x2000 对应一个 memblock_region,包含 base=0x1000size=0x1000flags=MEMBLOCK_NONE

(2)memblock_type

  • 作用:管理同一类内存区域的集合(如“可用内存”或“保留内存”)。

    • 字段

      • cnt:当前区域数量。

      • max:数组最大容量。

      • total_size:所有区域总大小。

      • regions:指向 memblock_region 数组的指针。

      • name:类型名称(如 memory)。

  • 示例
    memory 类型管理所有可用物理内存,包含两个 memblock_region(例如 0x0-0x9FFF 和 0x100000-0x1FFFFF)。

(3)memblock

  • 作用:全局内存分配器的顶层结构,整合所有 memblock_type 并控制分配策略。

    • 字段

      • bottom_up:分配方向(从低地址向高地址,或相反)。

      • current_limit:物理地址上限(例如限制为 32 位地址空间)。

      • memory 和 reservedmemblock_type 实例,分别管理可用和保留内存。

  • 示例
    系统初始化时,memblock 的 memory 类型记录所有物理内存,reserved 类型记录已分配的内存。


2. 协同工作流程

场景示例:系统启动时的内存管理

  1. 初始化 memblock

    • 系统启动时,memblock 初始化两个 memblock_type

      • memory:初始为空,后续由 BIOS 或固件报告填充。

      • reserved:初始为空,记录内核保留区域。

    • 设置 bottom_up=false(默认从高地址开始分配),current_limit 设置为最大物理地址。

  2. 添加物理内存到 memory 类型

    • BIOS 报告两段物理内存:

      • 0x0-0x9FFF(常规内存)

      • 0x100000-0x1FFFFF(扩展内存)

    • 创建两个 memblock_region,添加到 memory 的 regions 数组:

      // memblock_region 1: base=0x0, size=0xA000, flags=0
      // memblock_region 2: base=0x100000, size=0x100000, flags=0
    • memory 的 cnt=2total_size=0x1A0000

  3. 保留内核代码区域

    • 内核需要保留 0x500-0x1000 用于自身代码:

      memblock_reserve(0x500, 0xB00);  // 调用 memblock API
    • reserved 类型新增一个 memblock_region

      // memblock_region: base=0x500, size=0xB00, flags=0
    • reserved 的 cnt=1total_size=0xB00

  4. 分配内存给驱动程序

    • 请求分配 0x2000 大小的内存:

      void *addr = memblock_alloc(0x2000, MEMBLOCK_NOMAP);
    • memblock 从 memory 的 regions 中查找可用区域(例如 0x100000-0x1FFFFF)。

    • 分配后:

      • memory 的对应 region 被分割或缩小(例如剩余 0x102000-0x1FFFFF)。

      • 新分配的 0x100000-0x102000 添加到 reserved 的 regions

      • reserved 的 cnt=2total_size=0xB00+0x2000


3. 关键交互过程

  • memblock 控制全局策略:通过 bottom_up 决定分配方向,通过 current_limit 限制物理地址范围。

  • memblock_type 分类管理内存memory 维护可用内存池,reserved 跟踪已分配区域。

  • memblock_region 提供具体信息:每个区域的基础属性由 memblock_region 描述,并存储在 memblock_type 的数组中。

4.关系图如下:

4.ARM64内核初始化memblock分配器流程

        在源文件“mm/memblock.c”定义全局变量memblcok,把成员bottom_up初始化为假表示从高地址向下分配。

        ARM64 内核初始化 memblock 分配器过程:
                a. 解析设备树二进制文件中的节点 /memory,把所有物理内存范围添加到 memblock;
                b. 在函数 arm64_memblock_init 中初始化 memblock。

5.memblock分配器编程接口

        memblock_add:添加新的内存块区域到 memblock.memory 中;
        memblock_remove:删除内存块区域;
        memblock_alloc:分配内存;
        memblock_free:释放内存。

        具体源码分析如下: 

6.memblock内存分配器原理以及理解

        主要维护两种内存:第一种内存是系统可用的物理内存,即系统实际含有的物理内存,其值从 DTS 中进行配置,通过 uboot 实际探测之后传入到内核。第二种内存是内核预留给操作系统的内存,这部分内存作为特殊功能使用,不能作为共享内存使用。

一、系统可用的物理内存

  1. 定义
    这种内存是系统实际含有的物理内存,其值从 DTS(设备树)中进行配置,通过 uboot 实际探测之后传入到内核。
  2. 举例
    在基于 ARM 架构的嵌入式系统(如工业控制设备)中,设备有 1GB 物理内存。系统启动时,uboot 读取设备树中内存描述(如:
 
memory@80000000 {  
    device_type = "memory";  
    reg = <0x80000000 0x40000000>;  
}  

   reg表示内存起始地址(0x80000000)和大小(0x40000000,即 1GB)。uboot 探测确认后将信息传递给内核,内核后续用这 1GB 内存加载程序、分配进程等。

 

二、内核预留给操作系统的内存

  1. 定义
    内核预留给操作系统的内存,用于特殊功能,不能作为共享内存使用。
  2. 举例
    Linux 内核启动时预留内存(如 16MB)存放内核代码、数据结构。例如内核栈,当进程系统调用进入内核态,内核用预留内存创建内核栈,存储函数调用、局部变量等,用户进程无法将其作为共享内存使用,仅用于内核特定操作(系统调用、中断处理等)。

 bootmem、memblock分配器与slab、伙伴和不连续页分配器的关系:

         在 Linux 内核早期,bootmem 作为引导内存分配器,用于内核初始化阶段临时分配内存 。后来 memblock 逐渐取代 bootmem,成为新的引导内存分配器,它能更灵活地管理内存,处理内存热插拔、设备树内存描述解析等情况。

        在引导阶段结束后,memblock 通常会将内存的管理工作移交给伙伴分配器(Buddy Allocator) 。伙伴分配器主要负责管理连续物理页的分配,是内核物理内存分配的基础。而 slab 分配器主要针对小对象(如内核结构体)的缓存分配,不连续页分配器(如 vmalloc)用于分配虚拟地址连续、物理地址不连续的内存,它们在后续内存管理中根据不同需求发挥作用,但一般不是直接从引导内存分配器移交管理,而是在内核后续运行过程中按需参与内存分配工作。

https://github.com/0voice

http://www.dtcms.com/a/113603.html

相关文章:

  • 金仓数据库KCM认证考试介绍【2025年4月更新】
  • PgVectore的使用
  • REASONING THOUGHT和REASONING分别是什么意思,有什么区别
  • C语言:字符串
  • Baklib企业CMS的核心要素是什么?
  • 贪心算法之最小生成树问题
  • Sentinel实战(五)、系统保护规则、限流后统一处理及sentinel持久化配置
  • 多GPU训练
  • C++_类和对象(上)
  • 【简单数论】(模运算,快速幂,乘法逆元,同余,exgcd,gcd,欧拉函数,质数,欧拉筛,埃式筛,调和级数枚举,约数,组合数)
  • 4.4日欧篮联,NBA全扫盘,雷霆 vs 火箭单关预测已出
  • 来聊聊C++中的vector
  • C++学习之线程
  • [Android安卓移动计算]:新建项目和配置环境步骤
  • 力扣DAY35 | 热100 | LRU缓存
  • 在windows环境下通过docker-compose脚本自动创建mysql和redis
  • SQL Server常见问题的分类解析(二)
  • 分治-归并排序-逆序对问题
  • 计算机视觉图像处理基础系列:滤波、边缘检测与形态学操作
  • 小迪安全110-tp框架,版本缺陷,不安全写法,路由访问,利用链
  • Android使用OpenGL和MediaCodec渲染视频
  • AI浪潮下,“内容创作平台”能否借势实现内容价值跃升?
  • Turtle图形化编程知识点汇总:让编程更有趣
  • IDEA 2024.3.5 中修改 web.xml 的 Servlet 版本(比如从 4.0 修改为 5.0)
  • I.MX6ULL开发板与linux互传文件的方法--NFS,SCP,mount
  • AbstractBeanFactory
  • 基于SSM的车辆管理系统的设计与实现(代码+数据库+LW)
  • kd树和球树
  • Java中使用OpenCV实现怀旧滤镜时遇到的UnsatisfiedLinkError问题及解决方案
  • 一文读懂 MCP!