type
status
date
slug
summary
tags
category
icon
password
URL
linux 内存管理主要分 3 个阶段:
- MMU 未打开,还在汇编时代时;
- fixmap, memblock 时代,此时伙伴系统还未成形,一直到 mm_init() 函数中 mem_ini() 将空闲内存加载到 zone 中;
- 伙伴系统建立
start_kernel
start_kernel
中和内存管理相关的系统初始化函数主要如下:—->setup_arch: 体系机构的设置函数,还负责初始化自举分配器
—->setup_per_cpu_areas: 定义 per-cpu 变量, 为各个 cpu 分别创建一份这些变量副本
—->build_all_zonelists: 建立节点 (node) 和内存域 (zone) 的数据结构
—->mem_init: 停用 bootmem 分配器并迁移到实际的内存管理函数
—->kmem_cache_init: 初始化内核内部用于小块内存区的分配器
—->setup_per_cpu_pageset: 为各个 cpu 的 zone 的 pageset 数组的第一个数组元素分配内存
setup_arch
setup_arch
—->machine_specific_memory_setup: 创建一个列表,包括系统占据的内存区和空闲内存区
—->parse_early_param: 解析 dtb 树命令行
—->setup_memory: 确定每个节点可用的物理内存也数目,初始化 bootmem 分配器,分配各种内存区
—->paging_init: 初始化内核页表并启动内存分页
---->pagetable_init: 确保直接映射到内核地址空间的物理内存被初始化
—->zone_size_init: 初始化系统中所有节点的 pgdat_t 实例
---->add_active_range: 对可用的物理内存建立一个相对简单的列表
---->free_area_init_nodes: 建立完备的内核数据结构
本文主要内容
本篇主要介绍 memblock 建立过程及分页机制化,主要有如下几个步骤
- setup_machine_fdt: 解析 dtb,收集内存信息及 bootargs
- hoosen node。该节点有一个 bootargs 属性,该属性定义了内核的启动参数,而在启动参数中,可能包括了 mem=nn[KMG] 这样的参数项。initrd-start 和 initrd-end 参数定义了 initial ramdisk image 的物理地址范围。
- memory node。这个节点主要定义了系统中的物理内存布局。主要的布局信息是通过 reg 属性来定义的,该属性定义了若干的起始地址和 size 条目。
- DTB header 中的 memreserve 域。对于 dts 而言,这个域是定义在 root node 之外的一行字符串,例如:/memreserve/ 0x05e00000 0x00100000。
- reserved-memory node。这个节点及其子节点定义了系统中保留的内存地址区域。保留内存有两种 (1. 静态定义,用 reg 属性定义的 address 和 size; 2. 动态定义,通过 size 属性定义了保留内存区域的长度,或者通过 alignment 属性定义对齐属性)
- early_fixmap_init: 对保留的 fixmap 区域创建映射
- early_ioremap_init: 初始化 early_ioremap 机制
- arm64_memblock_init: 初始化 memblock 机制
- paging_init: 初始化内核页表,内存节点,内存域及页帧 page, 此函数功能较为复杂
- request_standard_resources:将 memblock.memory 挂载到 iomem_resource 资源树下
- early_ioremap_reset: 结束 early_ioremap 机制
- unflatten_device_tree: dtb 转换为 device_node tree
- 根据 device node tree 初始化 CPU,psci
关键函数分析
setup_arch
arm64_memblock_init
setup_arch->arm64_memblock_init
early_init_fdt_scan_reserved_mem
在看 paging_init() 函数前,我们先看下目前的内存状态
目前所有的内存分为了 2 部分
- OS 已经收集到的内存分布信息 (来自 dtb 解析),保存在 memblock 中,这部分又分为 3 个小部分
- 系统内存占据的空间,信息保存在 memblock.memory 中
- 已经使用或者保留使用的,信息保存在 memblock.reserve 中
- dtb 中 reserved-memory,但是有 No-map 属性,这种内存不属于 OS 管辖
- OS 还未收集到的内存部分,暂未管辖 (这部分稍后会被加载到伙伴系统中)
在目前状态下,OS 还无法正常使用它们,因为 memblock 中定义的都是物理地址;而目前仅有两段内存是已经 mapping 过 (kernel image, fdt), 其余段都还是黑暗状态,接下来就要给第一部分内存做 mapping
paging_init
buddy 系统初始化
到目前为止,内核完成了如下工作
- memblock 已经通过 arm64_memblock_init 完成了初始化, 至此系统中的内存可以通过 memblock 分配了
- paging_init 完成了分页机制的初始化, 至此内核已经布局了一套完整的虚拟内存空间
稀疏内存管理将整个物理地址空间划分为 section
- 对于 ARM64,一般支持 48bit 物理地址 (256T),section 为 1G 物理块,可以划分为 256K 个 seciton;
- 每个在位的 section 在软件上抽象为一个 struct mem_section 结构体;
- 对于每个 section 又可以分为若干 Pageblock,每个 pageblock 的状态由 4bit 来描述
bootmem_init
sparse_init
build_all_zonelists
build_all_zonelists->build_all_zonelists_init->__build_all_zonelists
参考资料:
- http://www.wowotech.net/memory_management/memory-layout.html
- http://www.wowotech.net/memory_management/mem_init_3.html