【概念】---------------------------------------------------------------------------------------------------------------------------
在 Linux 6.x 中,bootmem 被 memblock 机制取代
与 bootmem 的对比
灵活性: memblock 更加灵活,可以处理更加复杂的内存管理需求,不像 bootmem 那样简单且局限于初期内存分配。
延迟分配: memblock 支持更加延迟的内存分配,不会在系统启动初期就占用过多内存,这对于大型系统和多处理器系统尤为重要。
内存区域管理: memblock 更加注重物理内存的分区和动态管理,而 bootmem 更多地依赖于固定的内存池。
arm64_memblock_init就完成了arm64架构下的memblock的初始化。
地址空间划分
32位
用户
内核:kernel space
page_offset:0xC000_0000 即经常提到的3 GiB
线性映射区域
ZONE_DMA 内存开始的16MB
high_memory
内核地址空间
1. 内核映射区域(Kernel Mapping Area)
内核映射区域通常是内核空间的一部分,用于映射物理内存到内核虚拟地址空间。这一部分内存允许内核直接访问所有的物理内存资源,特别是需要频繁访问的硬件资源或设备内存。
在一些操作系统(如Linux、Windows等)中,内核通过该区域对硬件进行直接操作,进行设备驱动、内存管理和系统调用等任务。
2. I/O 内存映射(I/O Memory Mapping)
这部分内存也常常用于 I/O 内存映射。这意味着操作系统可以通过映射到虚拟内存的方式,直接访问硬件设备的内存区域。例如,GPU、硬盘控制器、网络适配器等硬件设备可能通过这种方式与操作系统进行交互。
I/O 内存区域的映射帮助操作系统高效地控制硬件设备,提高访问速度和减少延迟。
3. Page Table Management
在64位架构中,内核需要管理大量的页面表来映射虚拟内存到物理内存。最后128 MiB的内核地址空间可能用于存储这些页面表项,尤其是在大内存系统上,操作系统需要大量的内核空间来维护虚拟内存的映射。
一些操作系统(如Linux)使用这些区域来存储 页表 或其他内存管理结构,如页目录、页表项等。
4. CPU特定的系统区域
在某些硬件架构上(尤其是x86-64或ARM64等),最后128 MiB可能被预留给CPU特定的系统区域,比如控制寄存器、硬件中断向量、调度器等的使用空间。
例如,在x86架构中,内核地址空间的上部会预留一定的空间给 硬件异常处理、调度程序 或其他内核内部任务。
5. 保护区(Protection Area)
最后128 MiB也可以作为保护区,防止内核空间被用户程序恶意或无意间篡改。这一块区域可能会用于存储操作系统的关键数据结构,如 内核堆栈、内核调用栈等,用于提升系统的安全性。
这种保护机制有助于防止用户程序突破权限边界,直接干扰或攻击内核的工作。
6. 内核堆和栈区域(Kernel Heap & Stack)
一些操作系统在内核地址空间的最后部分划分出堆和栈区域,用于内核的内存分配和函数调用。与用户空间不同,内核的堆和栈是为内核任务(如进程调度、系统调用处理等)而专门设置的。
用户地址空间
+-------------------------+
| 栈 (Stack) | ← 高地址
|-------------------------|
| 堆 (Heap) |
|-------------------------|
| 数据段 (Data) |
|-------------------------|
| BSS段 (BSS) |
|-------------------------|
| 代码段 (Text) | ← 低地址
+-------------------------+
【边界】---------------------------------------------------------------------------------------------------------------------------
【阶段】---------------------------------------------------------------------------------------------------------------------------
在 Linux 6.x 中,bootmem 被 memblock 机制取代
第一阶段
系统启动--->bootmem或者memblock初始化完成 : 此阶段只能使用memblock_reserve函数分配内存, 早期内核中使用init_bootmem_done = 1标识此阶段结束
第二阶段
bootmem或者memblock初始化完--->buddy完成前: 引导内存分配器bootmem或者memblock接受内存的管理工作, 早期内核中使用mem_init_done = 1标记此阶段的结束
mm_core_init: 建立了内核的内存分配器,其中通过mem_init停用bootmem分配器并迁移到实际的内存管理器(比如伙伴系统),然后调用kmem_cache_init函数初始化内核内部用于小块内存区的分配器
mem_init
kmem_cache_init
第三阶段
buddy初始化完成--->系统停止运行: 可以用cache和buddy分配内存
kmem_cache_init_late: 在kmem_cache_init之后, 完善分配器的缓存机制, 当前3个可用的内核内存分配器slab, slob, slub都会定义此函数
memblock 的工作流程:
内存块初始化: 在内核引导的最初阶段,memblock 负责记录系统的内存布局,并将内存划分为不同的区域(比如 memblock.memory 和 memblock.reserved)。
内存区域划分: 在启动过程中,内存被划分为多个区域,这些区域可以在后续阶段按需进行分配。
内存分配: memblock 负责管理这段内存的初步分配,以支持内核启动阶段需要的基本内存分配。
memblock 并不仅仅是替代了 bootmem,它还与其他内存管理机制(如页表初始化、early pagetable)协同工作
early pagetable 初始化: 在内核引导阶段,memblock 用于管理物理内存区域,并配合早期页表(early page table)初始化,为虚拟内存管理和更复杂的内存分配提供必要支持。
memblock
页分配器(page_alloc)初始化: memblock 的初始化为后续的页分配器初始化提供了物理内存区域的详细信息,确保内存的页级别分配能够正确进行。
memblock的初始化
setup_arch
arm64_memblock_init( );/* 初始化memblock */
paging_init();/* 分页机制初始化 */
bootmem_init();/* 初始化内存 */
paging_init负责建立只能用于内核的页表
start_kernel()
|---->page_address_init()
| 考虑支持高端内存
| 业务:初始化page_address_pool链表;
| 将page_address_maps数组元素按索引降序插入
| page_address_pool链表;
| 初始化page_address_htable数组.
|
|---->setup_arch(&command_line);
| 初始化特定体系结构的内容
|---->arm64_memblock_init( ); [参见memblock和bootmem]
| 初始化引导阶段的内存分配器memblock
|
|---->paging_init(); [参见分页机制初始化paging_init]
| 分页机制初始化
|
|---->bootmem_init(); [与build_all_zonelist共同完成内存数据结构的初始化]
| 初始化内存数据结构包括内存节点和内存域
|
|---->setup_per_cpu_areas();
| 为per-CPU变量分配空间
|
|---->mm_core_init---->build_all_zonelist() [bootmem_init初始化数据结构, 该函数初始化zonelists]
| 为系统中的zone建立后备zone的列表.
| 所有zone的后备列表都在
| pglist_data->node_zonelists[0]中;
|
| 期间也对per-CPU变量boot_pageset做了初始化.
|
|---->mm_core_init---->page_alloc_init()
|---->hotcpu_notifier(page_alloc_cpu_notifier, 0);
| 不考虑热插拔CPU
|
|
|---->vfs_caches_init_early()
|---->dcache_init_early()
| dentry_hashtable空间,d_hash_shift, h_hash_mask赋值;
| 同pidhash_init();
| 区别:
| 散列度变化了(13 - PAGE_SHIFT);
| 传入alloc_large_system_hash的最后参数值为0;
|
|---->inode_init_early()
| inode_hashtable空间,i_hash_shift, i_hash_mask赋值;
| 同pidhash_init();
| 区别:
| 散列度变化了(14 - PAGE_SHIFT);
| 传入alloc_large_system_hash的最后参数值为0;
|
【状态】---------------------------------------------------------------------------------------------------------------------------
【信息】---------------------------------------------------------------------------------------------------------------------------
【行为】---------------------------------------------------------------------------------------------------------------------------
【原则】---------------------------------------------------------------------------------------------------------------------------
【规则】---------------------------------------------------------------------------------------------------------------------------
【问题】---------------------------------------------------------------------------------------------------------------------------
但是内存管理的数据结构都没有建立, 而这些数据结构创建的过程本身就是一个内存分配的过程, 那么就出现一个问题。
还没有一个内存管理器去负责分配和回收内存, 而又不可能将所有的内存信息都静态创建并初始化, 那么我们怎么分配内存管理器所需要的内存呢?
即因此在系统启动过程期间, 内核使用了一个额外的简化形式的内存管理模块早期的引导内存分配器(boot memory allocator–bootmem分配器)
或者memblock, 用于在启动阶段早期分配内存, 而在系统初始化完成后, 该分配器被内核抛弃, 然后初始化了一套新的更加完善的内存分配器。
1780

被折叠的 条评论
为什么被折叠?



