作者 | 宋宝华  责编 | 张文

头图 | CSDN 下载自视觉中国

出品 | CSDN(ID:CSDNnews)

内核文档 Documentation/arm64/memory.rst 描述了 ARM64 Linux 内核空间的内存映射情况,应该是此方面最权威文档。

以典型的 4K 页和 48 位虚拟地址为例,整个内核空间的虚拟地址分布如下:

从 ffff000000000000 到 ffff7fffffffffff 是一段针对物理地址的线性映射区,最大支持 128TB 的物理地址空间,这一段地址非常类似 ARM32 的 low memory 映射区。

我们看看这种情况下的页表,我们既可以用最终的【20:12】对应的 PTE 映射项,以 4K 为单位,进行虚拟地址到物理地址的映射;又可以以【29:21】对应的 PMD 映射项,以 2M 为单位,进行虚拟地址到物理地址的映射。

对于用户空间的虚拟地址而言,当我们进行的是 PMD 映射的时候,我们得到的是 Huge Page,ARM64 的 2MB 的 huge page,在虚拟和物理上都连续,它在实践工程中的好处是,可以减小 TLB miss。因为如果进行了 2MB 的映射,整个 2MB 不再需要 PTE,映射关系大为减小。

对于内核空间而言,从 ffff000000000000 到 ffff7fffffffffff 的这段虚拟地址,如果与物理地址进行的是一种 PMD 映射的话,显然也可以达到同样的效果。

但是,这不意味着它们就是 Huge Page。

众所周知,内核开机把物理地址往虚拟地址进行线性映射,并不意味着这片内存被内核拿走了,它只是进行了一种映射,以便日后调用 kmalloc(),get_free_pages()等 API 申请的内存是直接已经有虚实映射的。所以,即便内核进行的就是 PMD 映射,在内存的分割上,还是可以以 4K 为单位的:

所以,即便我们在内核空间进行 PMD 映射,里面的每个蓝色圆圈(一个 4K页),还是可以被单独分配的,这种分配可以是 kmalloc、vmalloc,用户态的 malloc 等。

内核态进行的 PMD 映射,不意味着相关的 2MB 成为了 huge page。

它纯粹只是为了服务于当内核以线性映射的虚拟地址访问该物理地址的时候(我们认为内核大多数时候是用这个线性映射的虚拟地址的),减小 TLB miss。

当然,更牛的情况下,内核应该也可以直接用【38:30】位的 PUD 来进行映射,这样映射关系是 1GB 的,则整个 1GB 后面占 TLB 的时候,只需要占一个入口。

当然,如果用户态的虚实映射是这样的,用户实际得到了一个 1GB 的巨页。

但是对于内核的线性映射区域而言,即便我们进行了 1GB 的 PUD 映射,这 1G 内部就可以进一步切割为 4KB 页或者 2MB 的巨页。

记住:内核态的线性映射区的映射只是个映射关系,不是个分配关系。比如下面的 1GB 的内核线性映射的 1GB 区域,仍然可以被 4K 分配走,或者被用户以 huge page 以 2MB 为单位分配走:

我们需要一个真实的调试手段来验证我们的想法,这个调试手段就是 PTDUMP(Page Table Dump),相关的代码在 ARM64 内核的:

arch/arm64/mm/ptdump.c和ptdump_debugfs.c

我们把它们全部选中,这样我们可以得到一个 debugfs 接口:

/sys/kernel/debug/kernel_page_tables

来获知内核态页表的情况。

我用 qemu 启动了一个 4GB 内存的 ARM64 虚拟机,可以看到前 1GB 的虚拟地址空间大多数是 PMD 和 PTE 映射,后面的 3GB,全是 PUD 映射:

我的内核启动参数加了 rodata=0:

$ cat /proc/cmdline
root=/dev/vda2 rw console=ttyAMA0  ip=dhcp rodata=0

原因是内核在几种情况下,是不会做这种 PMD 和 PUD 映射的,相关代码见于:

rodata_full 在默认情况下总是成立的,它对应着内核的一个 Config 选项 CONFIG_RODATA_FULL_DEFAULT_ENABLED, "Apply r/o permissions of VM areas also to their linear aliases",这个选项提高了内核的安全性,但是减小了内核的性能。

我在内核启动参数加的 rodata=0 实际上是让 rodata_full 为 false。

如果我把这个 kernel 启动选项去掉,我得到的内核页表是完全不一样,线性映射区也全部是 PTE 映射:

最后,值得一提的是,不仅线性映射区可以使用 PMD 映射,vmemmap 映射区也是在 4K 页面情况下,默认用 PMD 映射的:

字节跳动的宋牧春童鞋发了一个 patchset,企图在用户分得巨页的情况下,删除巨页内部的 4KB 的小 page 占用的 page struct 的内存消耗,这个 patchset 在圣诞节前目前发到了 V11:

https://lore.kernel.org/linux-mm/20201222142440.28930-1-songmuchun@bytedance.com/

在这个 patchset 中,它就需要拆分 vmemmap 的 PMD 映射为 PTE 映射:

这个 patchset 的原理建立在,当内核以 4KB 分页的时候,每个 page 需要 64 字节的 page struct。

但是,当用户把它分配为巨页的时候,我们不再需要一个个 4KB 单独用 page struct 描述,对于这种 compound page 的情况,我们应该可以把后面的 page struct 的内存直接释放掉,因为情况完全是雷同的,这样可以省下不少内存。

看到牧春童鞋从一个青葱少年成长为这方面的大牛,我真的替他高兴和骄傲。这同时也激励我必须保持奋斗姿态,2021 年要不停歇地学习进步,争取赶上牧春童鞋。

程序员如何避免陷入“内卷”、选择什么技术最有前景,中国开发者现状与技术趋势究竟是什么样?快来参与「2020 中国开发者大调查」,更有丰富奖品送不停!

戳:https://bss.csdn.net/m/topic/dev_survey2020

ARM64 Linux 内核页表的块映射相关推荐

  1. Linux内核页表管理-那些鲜为人知的秘密

    1.开场白 环境: 处理器架构:arm64 内核源码:linux-5.11 ubuntu版本:20.04.1 代码阅读工具:vim+ctags+cscope 通用操作系统,通常都会开启mmu来支持虚拟 ...

  2. QEMU启动ARM64 Linux内核

    目录 前言 前置知识 virt开发板 ARM处理器家族简介 安装qemu-system-aarch64 安装交叉编译工具 交叉编译ARM64 Linux内核 交叉编译ARM64 Busybox 使用b ...

  3. ARM DS-5单步调试ARM64 linux 内核

    目录 1 介绍 2 开发环境 3 准备工作 3.1 Ubuntu环境准备 3.2 源代码准备 3.3 DS-5准备 3.4 使用DS-5调试源码 3.4.1 建立源码工程 3.4.2 创建debug配 ...

  4. 深入理解Linux内核页表映射分页机制原理

    前言 操作系统用于处理内存访问异常的入口操作系统的核心任务是对系统资源的管理,而重中之重的是对CPU和内存的管理.为了使进程摆脱系统内存的制约,用户进程运行在虚拟内存之上,每个用户进程都拥有完整的虚拟 ...

  5. 嵌入式ARM64 Linux内核FIT uimage方式启动

    平台:orangepi4 rockchip rk3399 LPDDR4 4G eMMC 16G 系统:ubuntu 20.04 FIT简介 device tree在ARM架构中普及之后,u-boot也 ...

  6. 从 Java sleep 来看 arm64 Linux 内核都干了些什么?

    使当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,取决于系统计时器和调度器的精度和准确性.线程不会失去任何监视器的所有权. sleep(long millis) 仅仅调用 sleep 带两个参数版 ...

  7. ARM64 Linux内核起始虚拟地址

    0xFFFFFE0000000000  是ARM64 Linux起始的虚拟地址,第一个虚拟地址

  8. Vscode 调试arm64 linux内核

    对于linux内存系列的阅读和测试记录. https://zhuanlan.zhihu.com/p/105069730 https://zhuanlan.zhihu.com/p/510289859 搭 ...

  9. linux 内核页表 tlb,Linux中的mips64 tlb管理

    关于tlb的描述可参考mips run2以及mips64官方手册. tlb条目:tlb entry 寄存器:entryHi, entryLo0, entryLo1, mask, index, wire ...

最新文章

  1. 企业运维监控平台架构设计与实现
  2. 【Android 逆向】x86 汇编 ( align | db | dw | dd | nop | 伪指令 )
  3. 在字符串中查找指定的字符串--strstr
  4. SAP概念之Client(集团)
  5. readUnsignedInt () 自动移动字节流位置,和.net是一样的
  6. 数据库不完全恢复 以及恢复到测试环境:
  7. Spring 框架基础(01):核心组件总结,基础环境搭建
  8. 【原】获取数据库(SQL SERVER 2005)的所有信息 Get all database information from SQL Server 2005 测试通过...
  9. linux虚拟主机_云服务器与虚拟主机的区别
  10. 第十周学习总结--助教
  11. 模式识别、机器学习的区别和联系
  12. SysTrace常识
  13. 无法打开Internet站点
  14. SqlCommandBuilder 批量更新数据库的怪异问题?
  15. 精通RPM之--制作篇(上)
  16. JavaWEB十五:QQzone项目的整体分析及web关键点总结
  17. 2021美赛B题翻译
  18. 谈谈企业的持续交付流水线设计
  19. Mysql统计近30天的数据,无数据的填充0
  20. python读取7个数(1-50)的整数值_python每日一题总结7

热门文章

  1. 【Spring Cloud】网关-gateway(2.x)
  2. 学生信息管理系统问题集锦(二)
  3. Java内存模型三大特性
  4. Java的互斥同步机制
  5. 敏捷开发一千零一问系列之六:业务人员怎样参与开发?
  6. Android 2019最新面试实战总结
  7. Android的广播接收器BroadcastReceiver
  8. 笔记《JavaScript 权威指南》(第6版) 分条知识点概要1—词法结构
  9. ThinkPHP5中的助手函数
  10. dubbo 学习笔记 -- provider端