Linux内核 内存管理 物理内存初始化流程
1.ARM64页表初始化流程图
start_kernel()│▼
setup_arch() // 架构相关初始化│▼
early_fixmap_init() // 初始化Fixmap(临时映射设备树等)│▼
arm64_memblock_init() // 从设备树解析内存布局│▼
arm64_memblock_reserve() // 保留内核镜像、initrd等区域│▼
paging_init() // 核心页表初始化函数├─▶ pgd_set_fixmap() // 获取swapper_pg_dir的物理地址├─▶ map_kernel() // 映射内核代码段(.text, .data等)│ ├─▶ __create_pgd_mapping()│ │ ├─▶ PGD → PUD → PMD → 填充大页表项(2MB/1GB)│ │ └─▶ 设置内存属性(如可缓存/设备内存)│├─▶ map_mem() // 线性映射所有物理内存│ ├─▶ for_each_mem_range()│ └─▶ __create_pgd_mapping() // 遍历所有内存区域并映射│├─▶ cpu_replace_ttbr1() // 切换TTBR1寄存器到新页表│ └─▶ load_ttbr1() // 加载swapper_pg_dir到TTBR1│└─▶ pgd_clear_fixmap() // 清理临时映射│▼
free_all_bootmem() // 将内存释放到伙伴系统
Fixmap初始化 (early_fixmap_init)
建立临时映射,用于访问设备树(DTB)等关键数据。
内存探测 (arm64_memblock_init)
从设备树解析物理内存布局(如memory@40000000节点)。
保留关键区域 (arm64_memblock_reserve)
保留内核镜像、initrd、设备树等区域不被分配。
页表初始化 (paging_init)
映射内核代码段:将内核的代码段(_text到_end)映射到虚拟地址空间。
线性映射物理内存:将物理地址0x0到最大物理地址映射到虚拟地址PAGE_OFFSET(如0xffffff8000000000)。
使用大页:默认使用2MB或1GB大页,减少页表层级。
切换页表寄存器 (cpu_replace_ttbr1)
将新页表基地址(swapper_pg_dir)写入TTBR1寄存器,完成页表切换。
释放内存到伙伴系统 (free_all_bootmem)
将memblock中的内存转移到伙伴系统,完成内存管理初始化。
关键数据结构
物理内存管理:struct page, struct zone, struct pglist_data。
分配器:伙伴系统(free_area)、SLAB(kmem_cache)。
虚拟内存:vm_area_struct, mm_struct。
页表:pgd_t, pte_t等页表项。
高级功能:反向映射(anon_vma)、内存控制组(mem_cgroup)
2.ARM64架构内存初始化
文件路径: arch/arm64/mm/init.c
关键函数: bootmem_init() → paging_init()
代码逻辑:
// ARM64内存初始化流程
void __init bootmem_init(void) {// 1. 从设备树解析内存信息arm64_memblock_init();// 2. 保留内核镜像、initrd等区域arm64_memblock_reserve();// 3. 初始化页表并建立线性映射paging_init();// 4. 将内存释放到伙伴系统free_all_bootmem();}// 页表初始化
void __init paging_init(void) {// 创建swapper_pg_dir页表map_kernel(); // 映射内核代码段map_mem(); // 映射所有物理内存cpu_replace_ttbr1(lm_alias(swapper_pg_dir));}
1.从设备树解析内存布局 → 2. 保留关键区域 → 3. 初始化memblock → 4. 线性映射物理内存 → 5. 移交内存到伙伴系统。
1.从设备树解析内存信息
start_kernel()->setup_arch()->setup_machine_fdt()->early_init_dt_scan_nodes()->early_init_dt_scan_memory()
解析“memory”描述的信息从而得到内存的base_address和size信息,最后内存块信息通过early_init_dt_add_memory_arch ()->memblock_add()函数添加到memblock子系统中。
start_kernel()→ setup_arch(&command_line)→ setup_machine_fdt(__fdt_pointer) // 解析设备树→ early_init_dt_scan_nodes()→ early_init_dt_scan_memory() // 扫描设备树中的"memory"节点
2.物理内存映射
在内核使用内存前,需要初始化内核的页表,初始化页表主要在map_lowmem()函数中。在映射页表之前,需要把页表的页表项清零,主要在prepare_page_table()函数中实现。
void __init paging_init(void)
{phys_addr_t pgd_phys = early_pgtable_alloc(); // 分配PGD页表物理内存pgd_t *pgd = pgd_set_fixmap(pgd_phys); // 临时映射PGD页表到Fixmap区域map_kernel(pgd); // 映射内核镜像(代码、数据段等)map_mem(pgd); // 线性映射所有物理内存cpu_replace_ttbr1(lm_alias(pgd)); // 切换TTBR1到新页表init_mm.pgd = (pgd_t *)lm_alias(pgd); // 更新init_mm结构的PGD指针pgd_clear_fixmap(); // 清除PGD的临时Fixmap映射memblock_free(pgd_phys, PAGE_SIZE); // 释放临时PGD页表内存cpu_replace_ttbr1(NULL); // 确保TTBR1切换完成(屏障操作)
}
关键子函数与操作解析
1. early_pgtable_alloc()
作用:分配一个物理页用于存储顶级页表(PGD)。
实现细节:
调用memblock_alloc()从memblock分配器分配一个4KB页。
该页用于存储swapper_pg_dir(内核初始页表)。
2. pgd_set_fixmap(pgd_phys)
作用:将物理地址pgd_phys的PGD页表临时映射到内核Fixmap区域。
Fixmap区域:
一段预定义的虚拟地址空间,用于临时映射物理内存(如页表、设备树等)。
通过fixmap_remap_fdt()等函数使用。
3. map_kernel(pgd)
作用:映射内核镜像到虚拟地址空间,包括代码段(.text)、数据段(.data)、BSS段(.bss)等。
关键操作:
调用__create_pgd_mapping()创建页表项。
使用大页(如2MB或1GB)映射以减少页表层级。
映射范围:
内核的虚拟地址范围从_text到_end(通过链接脚本定义)。
4. map_mem(pgd)
作用:线性映射所有物理内存到内核虚拟地址空间。
实现细节:
遍历memblock.memory中的所有内存区域。
调用__create_pgd_mapping()为每个区域创建页表项。
物理地址phys → 虚拟地址__phys_to_virt(phys)(如0xffffff8000000000 + phys)。
特殊处理:
跳过标记为MEMBLOCK_NOMAP的内存区域(如设备树中的no-map属性区域)。
5. cpu_replace_ttbr1(pgd)
作用:将新的页表基地址(PGD)加载到TTBR1寄存器,完成页表切换。
关键操作:
调用load_ttbr1()函数写入TTBR1寄存器。
执行dsb(ishst)和isb()屏障指令确保操作完成。
ARM64地址空间划分:
TTBR0:用户空间页表基地址(低地址空间)。
TTBR1:内核空间页表基地址(高地址空间)。
6. pgd_clear_fixmap()
作用:清除PGD页表的Fixmap临时映射,释放Fixmap区域供后续使用。
页表映射的底层操作(__create_pgd_mapping)
map_kernel() 和 map_mem() 最终调用 __create_pgd_mapping() 创建页表项。其核心流程如下:
遍历页表层级:PGD → P4D → PUD → PMD → PTE。
分配页表页:若中间页表不存在,则分配物理页并填充。
设置页表项:
大页映射(如2MB或1GB)直接设置PMD或PUD项。
普通页映射(4KB)需要填充PTE项。
设置内存属性:
内核代码段:可执行、可读、不可写(MT_NORMAL)。
设备内存:不可缓存、不可执行(MT_DEVICE_nGnRnE)。
关键数据结构
pgd_t:顶级页表项(Page Global Directory)。
pud_t:页上级目录(Page Upper Directory)。
pmd_t:页中间目录(Page Middle Directory)。
pte_t:页表项(Page Table Entry)。
3.初始化页表paging_init
// arch/arm64/mm/mmu.c
void __init paging_init(void) {// 初始化swapper_pg_dirpgd_t *pgdp = pgd_set_fixmap(__pa_symbol(swapper_pg_dir));map_kernel(pgdp); // 映射内核代码/数据map_lowmem(); // 映射低端内存pgd_clear_fixmap();// ... 其他映射(如vmalloc、固定映射等)}
在 C 语言阶段(start_kernel → setup_arch → paging_init),内核构建完整的页表,取代临时页表。
关键步骤:
1.初始化 swapper_pg_dir:
正式内核页表基址,替代临时页表 init_pg_dir。
pgd_t *swapper_pg_dir = (pgd_t *)__pa_symbol(init_pg_dir); // 早期共享临时页表
2.映射内核代码和数据(map_kernel):
将内核镜像(_text 到 _end)映射到虚拟地址空间(可能包含 KASLR 偏移)。
__create_pgd_mapping(swapper_pg_dir, kernel_start, __phys_to_virt(kernel_start),
kernel_end - kernel_start, PAGE_KERNEL_ROX, NO_BLOCK_MAPPINGS);
3.线性映射低端内存(map_lowmem):
建立物理内存的线性直接映射(VA = PA + PAGE_OFFSET),覆盖所有可用内存区域。
for_each_memblock(memory, reg) {
__create_pgd_mapping(swapper_pg_dir, reg->base, __phys_to_virt(reg->base),
reg->size, PAGE_KERNEL, NO_CONT_MAPPINGS);
}
4.特殊区域映射:
vmalloc 区域:预留虚拟地址空间(VMALLOC_START 到 VMALLOC_END),动态分配时填充页表。
固定映射(Fixmap):用于临时映射设备寄存器(如 earlycon 串口)。
IO 映射:通过 ioremap 将设备内存映射到内核空间。
5.切换至正式页表:
更新 TTBR1_EL1 指向 swapper_pg_dir,刷新 TLB 和缓存
cpu_replace_ttbr1(lm_alias(swapper_pg_dir), init_mm.context.asid);
6.流程简要:
启动(物理地址模式)│├─ 汇编阶段(head.S)│ ├─ 配置控制寄存器(TCR_EL1, MAIR_EL1)│ ├─ 分配临时页表(init_pg_dir)│ └─ 创建恒等映射(内核镜像、页表自身、FDT)│├─ 启用 MMU(设置 SCTLR_EL1.M)│└─ C 语言阶段(paging_init)├─ 初始化 swapper_pg_dir├─ 映射内核代码(map_kernel)├─ 线性映射低端内存(map_lowmem)├─ 映射特殊区域(Fixmap、vmalloc)└─ 切换至正式页表(TTBR1_EL1)
5.zone的初始化
Zone初始化在内核启动过程中完成,主要分为以下阶段:
1.物理内存探测
设备树/ACPI解析:从设备树(DTB)或ACPI表中获取物理内存布局。
memblock初始化:通过memblock_add()和memblock_reserve()标记可用和保留的内存区域。
关键函数:
start_kernel()
→ setup_arch()
→ arm64_memblock_init() // 解析设备树,初始化memblock
2. 内存区域划分(Zone划分)
确定各Zone的物理地址范围:
ZONE_DMA:通常为物理地址0x0到某个上限(如4GB)。
ZONE_NORMAL:剩余物理内存。
关键函数:
bootmem_init()
→ zone_sizes_init() // 计算各Zone的起始页号和页数
ARM64实现:
// arch/arm64/mm/init.c
void __init zone_sizes_init(void) {unsigned long max_zone_pfn[MAX_NR_ZONES] = {0};// 遍历所有内存区域,确定各Zone的最大页号for_each_memblock(memory, reg) {phys_addr_t start = reg->base;phys_addr_t end = start + reg->size;unsigned long start_pfn = PFN_DOWN(start);unsigned long end_pfn = PFN_DOWN(end);if (start_pfn < max_dma_pfn)max_zone_pfn[ZONE_DMA] = max(max_zone_pfn[ZONE_DMA], end_pfn);elsemax_zone_pfn[ZONE_NORMAL] = max(max_zone_pfn[ZONE_NORMAL], end_pfn);}// 调用free_area_init()初始化Zonefree_area_init(max_zone_pfn);}
3. Zone数据结构初始化
关键函数:free_area_init_node()
为每个NUMA节点(Node)初始化其包含的Zone。
分配并初始化struct zone和struct pglist_data(Node描述符)。
流程:
分配pglist_data结构:描述一个NUMA节点的内存信息。
初始化Zone的free_area:伙伴系统的空闲链表。
计算水位线(Watermark):min, low, high,用于内存回收和分配策略。
初始化每CPU页面集(Per-CPU Pageset):加速单页分配。
代码路径:
free_area_init()
→ free_area_init_node(0, max_zone_pfn, ...) // 单NUMA节点
→ calculate_node_totalpages() // 计算Node的总页面数
→ free_area_init_core() // 初始化每个Zone
4. 伙伴系统(Buddy System)初始化
将memblock内存释放到伙伴系统:
调用memblock_free_all()将memblock管理的可用内存释放到伙伴系统的空闲链表中。
关键函数:
mem_init()
→ memblock_free_all() // 释放所有未被保留的内存到伙伴系统
→ free_low_memory_core_early()
→ __free_memory_core() // 按页释放到伙伴系统
5.关键数据结构
1. struct zone
描述一个内存区域的核心属性:
struct zone {
unsigned long watermark[NR_WMARK]; // 内存水位线(min, low, high)
struct free_area free_area[MAX_ORDER]; // 伙伴系统的空闲块链表
spinlock_t lock; // 并发锁
unsigned long managed_pages; // 被伙伴系统管理的页面数
const char *name; // Zone名称(如"DMA")
// 统计信息(活跃页、非活跃页等)
};
2. struct pglist_data(pg_data_t)
描述一个NUMA节点的内存布局:
typedef struct pglist_data {
struct zone node_zones[MAX_NR_ZONES]; // 节点包含的Zone
struct zonelist node_zonelists[MAX_ZONELISTS]; // 备用Zone列表(分配回退)
int nr_zones; // Zone数量
unsigned long node_start_pfn; // 节点的起始物理页号
unsigned long node_spanned_pages; // 节点总物理页数(含空洞)
unsigned long node_present_pages; // 实际可用物理页数(不含空洞)
// 其他统计信息和锁
} pg_data_t;
相关文章:
Linux内核 内存管理 物理内存初始化流程
1.ARM64页表初始化流程图 start_kernel()│▼ setup_arch() // 架构相关初始化│▼ early_fixmap_init() // 初始化Fixmap(临时映射设备树等)│▼ arm64_memblock_init() // 从设备树解析内存布局│▼ arm…...
PyBluez2 的详细介绍、安装指南、使用方法及配置说明
PyBluez2:Python 蓝牙开发的核心库 一、PyBluez2 简介 PyBluez2 是 Python 的开源蓝牙编程库,支持蓝牙 2.0、BLE(低功耗蓝牙)和传统蓝牙协议栈的开发。它提供了对蓝牙硬件适配器的底层控制,适用于设备发现、配对、数…...
通过一个led点灯的demo来熟悉openharmony驱动编写的过程(附带hdf详细调用过程)
概述 本应用程序(led_rgb)是在上实现直接通过消息机制与内核驱动进行交互,设置RGB三色灯的亮灯行为。我从网上随便找了个demo测试了一下,坑了三天…,整个状态如下图,同时也迫使我深度梳理了一下整个流程框架。直到绝望的时候&…...
pycharm2024.1.1版本_jihuo
目录 前置: 步骤: step one 下载软件 step two 卸载旧版本 1 卸载软件 2 清除残余 step three 下载补丁 step four 安装2024.1.1版本软件 step five 安装补丁 1 找位置放补丁 2 自动设置环境变量 step six 输入jihuo码 前置: 之…...
目标检测20年(四)——最终章
欢迎各位读者尽情阅读前三篇文献解读。这一篇将会介绍文献的第五部分:目标检测近些年的新技术发展以及第六部分:总结与未来展望。这也是本篇论文解读的最后一篇文章。 目录 五、目标检测最新进展 5.1 不采用滑动窗口的检测 5.2 旋转和尺度变化的鲁棒性…...
PyTorch处理数据--Dataset和DataLoader
在 PyTorch 中,Dataset 和 DataLoader 是处理数据的核心工具。它们的作用是将数据高效地加载到模型中,支持批量处理、多线程加速和数据增强等功能。 一、Dataset:数据集的抽象 Dataset 是一个抽象类,用于表示数据集的接口。你…...
【Linux】POSIX信号量与基于环形队列的生产消费者模型
目录 一、POSIX信号量: 接口: 二、基于环形队列的生产消费者模型 环形队列: 单生产单消费实现代码: RingQueue.hpp: main.cc: 多生产多消费实现代码: RingQueue.hpp: main.…...
Spring Boot 连接 MySQL 配置参数详解
Spring Boot 连接 MySQL 配置参数详解 前言参数及含义常用参数及讲解和示例useUnicode 参数说明: 完整配置示例注意事项 前言 在 Spring Boot 中使用 Druid 连接池配置 MySQL 数据库连接时,URL 中 ? 后面的参数用于指定连接的各种属性。以下是常见参数…...
[linux] linux基本指令 + shell + 文件权限
目录 1. Linux的认识 1.1. Linux的应用场景 1.2. Linux的版本问题 1.3. 操作系统的认识 1.4. 常用快捷键 2. 常用指令介绍 2.1. ADD 2.1.1. touch [file] 2.1.1.1. 文件的属性信息 2.1.2. mkdir [directory] 2.1.3. cp [file/directory] 2.1.4. echo [file] 2.1.4.…...
查看进程文件描述符的限制
查看进程文件描述符限制 rootgb:/home/gb/Monitor-Device-Mgr/Monitor-Device-Mgr/bin# ps -ef |grep Monitor-Device-Mgr root 3976 2380 59 11:10 pts/2 00:00:06 ./Monitor-Device-Mgr root 4010 2395 0 11:10 pts/3 00:00:00 grep --colorauto Monito…...
Python实现小红书app版爬虫
简介:由于数据需求的日益增大,小红书网页版已经不能满足我们日常工作的需求,为此,小编特地开发了小红书手机版算法,方便大家获取更多的数据,提升工作效率。 手机版接口主要包括:搜素࿰…...
【docker】docker-compose安装RabbitMQ
docker-compose安装RabbitMQ 1、配置docker-compose.yml文件(docker容器里面的目录请勿修改)2、启动mq3、访问mq4、查看服务器映射目录5、踩坑5.1、权限不足 1、配置docker-compose.yml文件(docker容器里面的目录请勿修改) versi…...
playwright-go实战:自动化登录测试
1.新建项目 打开Goland新建项目playwright-go-demo 项目初始化完成后打开终端输入命令: #安装项目依赖 go get -u github.com/playwright-community/playwright-go #安装浏览器 go run github.com/playwright-community/playwright-go/cmd/playwrightlatest insta…...
LeetCode hot 100 每日一题(13)——73. 矩阵置零
这是一道难度为中等的题目,让我们来看看题目描述: 给定一个 _m_ x _n_ 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 提示: m matrix.lengthn matrix[0].length1 < m, n …...
CEF 给交互函数, 添加控制台是否显示交互参数log开关
CEF 控制台添加一函数,枚举 注册的供前端使用的CPP交互函数有哪些 CEF 多进程模式时,注入函数,获得交互信息-CSDN博客 这两篇文章,介绍了注入函数,在控制台中显示 各自提供的交互函数信息。 有些场景下,我们还需要更详细的信息,比如想知道 彼此传递的参数, 如果每次调…...
云端存储新纪元:SAN架构驱动的智能网盘解决方案
一、企业存储的"不可能三角"破局 1.1 传统存储架构的困局 性能瓶颈:NAS架构在1000并发访问时延迟飙升300%容量限制:传统RAID扩容需停机维护,PB级存储扩展耗时超48小时成本矛盾:全闪存阵列每TB成本高达$3000࿰…...
PVE 安装黑苹果 MacOS
背景 我需要一台黑苹果,登录我不常用苹果账号。 方法 The Definitive Guide to Running MacOS in ProxmoxRunning a MacOS 15 Sequoia VM in ProxMox VE及视频 按照第二个的视频一步一步配置,第一个链接提供了不同版本OS...
Unity URP自定义Shader支持RenderLayer
前言: 当我们想用一个灯光只对特定的物体造成影响,而不对其余物体造成影响时,我们就需要设置相对应的LightLayer,但是这在URP12.0是存在的,在之后就不存在LightLayer这一功能,URP将其隐藏而改成了RenderLa…...
Axure项目实战:智慧城市APP(完整交互汇总版)
亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢! 课程主题:智慧城市APP 主要内容:主功能(社保查询、医疗信息、公交查询等)、活动、消息、我的页面汇总 应用场景ÿ…...
LVS-DR模式配置脚本
LVS-DR模式配置脚本 实验环境,需要4台虚拟机 IP说明172.25.254.101客户端172.25.254.102负载均衡器DS172.25.254.103真实服务器RS172.25.254.104真实服务器RSVIP:172.25.254.255/32 系统必须有ipvsadm和ifconfig命令 dnf install ipvsadm dnf install n…...
树状数组 3 :区间修改,区间查询
【题目描述】 这是一道模板题。 给定数列 a[1],a[2],…,a[n],你需要依次进行q个操作,操作有两类: 1lrx:给定 l,r,x对于所有 i∈[l,r],将a[i]加上x(换言之,将 a[l],a[l1],…a[r] 分别加上 x&a…...
架构思维:预约抢茅子架构设计
文章目录 案例:预约抢茅子复杂度分析商品预约阶段等待抢购阶段商品抢购阶段订单支付阶段 技术方案商品预约阶段一、基于 Redis 单节点的分布式锁方案1. 核心流程2. 关键设计点 二、Redis 单节点方案的局限性1. 单点故障风险2. 主从切换问题 三、多节点 Redis 实现高…...
使用 gone.WrapFunctionProvider 快速接入第三方服务
项目地址:https://github.com/gone-io/gone 本文中源代码: esexamples/es 文章目录 1. gone.WrapFunctionProvider 简介2. 配置注入实现3. 实战示例:Elasticsearch 集成4. 使用方式5. 最佳实践6. 总结 在如何给Gone框架编写Goner组件…...
基于SpringBoot+Vue的在教务管理(课程管理)系统+LW示例
1.项目介绍 系统角色:管理员、学生、教师功能模块:管理员(学院管理、专业管理、班级管理、学生管理、教师管理、课程管理、选课修改)、教师(授课查询、教师课表、成绩录入)、学生(选修课程、学…...
gitee 常用指令
1.拉取代码 // http git clone http.........// https git clone https......... 2. 设置自己账户和密码 ----- 绑定git git config --global user.name "你的用户名"git config --global user.email "你的邮箱" 3. 上传本地代码至git git initgit r…...
etcd性能测试
etcd性能测试 本文参考官方文档完成etcd性能测试,提供etcd官方推荐的性能测试方案。 1. 理解性能:延迟与吞吐量 etcd 提供稳定、持续的高性能。有两个因素决定性能:延迟和吞吐量。延迟是完成一项操作所花费的时间。吞吐量是在某个时间段内…...
JIRA/Xray测试管理工具的最佳实践:从基础到高阶的全场景指南
引言:测试管理的数字化转型与工具价值 在数字化时代,软件质量已成为企业竞争力的核心指标。然而,传统的测试管理方式——如Excel记录用例、邮件沟通缺陷、手动执行回归测试——已无法满足快速迭代的敏捷开发需求。据统计,全球因测…...
ubuntu桌面图标异常——主目录下的所有文件(如文档、下载等)全部显示在桌面
ubuntu桌面图标异常 问题现象问题根源系统级解决方案方法一:全局修改(推荐多用户环境)方法二:单用户修改(推荐个人环境)操作验证与调试避坑指南扩展知识参考文档问题现象 主目录文件异常显示 用户主目录(如/home/user/)下的所有文件(如文档、下载等)全部显示在桌面,…...
AIP-191 文件和目录结构
编号191原文链接https://google.aip.dev/191状态批准创建日期2019-07-25更新日期2019-07-25 统一的文件和目录结构,虽然在技术上差别不大,但可以让用户和审查者更容易阅读API界面定义。 指南 注意 以下指南适合于使用protobuf定义的API,例如…...
sql结尾加刷题
找了一下mysql对extractvalue()、updatexml()函数的官方介绍https://dev.mysql.com/doc/refman/5.7/en/xml-functions.html#function_extractvalue ExtractValue(xml_frag, xpath_expr) 知识点 解释一下这两个参数xml_frag,是xml标记片段,第二个参数…...
