0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

ARM64 Linux内核页表的块映射

Linux阅码场 来源:Linuxer 作者:Linuxer 2021-01-04 13:37 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

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

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

173d710c-4e4d-11eb-8b86-12bb97331649.png

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

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

1764476e-4e4d-11eb-8b86-12bb97331649.png

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

178dd1ce-4e4d-11eb-8b86-12bb97331649.png

对于内核空间而言,从ffff000000000000到ffff7fffffffffff的这段虚拟地址,如果与物理地址进行的是一种PMD映射的话,显然也可以达到同样的效果。但是,这不意味着它们就是Huge Page。众所周知,内核开机把物理地址往虚拟地址进行线性映射,并不意味着这片内存被内核拿走了,它只是进行了一种映射,以便日后调用kmalloc(),get_free_pages()等API申请的内存是直接已经有虚实映射的。所以,即便内核进行的就是PMD映射,在内存的分割上,还是可以以4K为单位的:

17c8ca68-4e4d-11eb-8b86-12bb97331649.png

所以,即便我们在内核空间进行PMD映射,里面的每个蓝色圆圈(一个4K页),还是可以被单独分配的,这种分配可以是kmalloc、vmalloc,用户态的malloc等。内核态进行的PMD映射,不意味着相关的2MB成为了huge page,它纯粹只是为了服务于当内核以线性映射的虚拟地址访问该物理地址的时候(我们认为内核大多数时候是用这个线性映射的虚拟地址的),减小TLB miss。

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

1835a250-4e4d-11eb-8b86-12bb97331649.png

当然,如果用户态的虚实映射是这样的,用户实际得到了一个1GB的巨页。但是对于内核的线性映射区域而言,即便我们进行了1GB的PUD映射,这1G内部就可以进一步切割为4KB页或者2MB的巨页。记住:内核态的线性映射区的映射只是个映射关系,不是个分配关系。比如下面的1GB的内核线性映射的1GB区域,仍然可以被4K分配走,或者被用户以huge page以2MB为单位分配走:

185868b2-4e4d-11eb-8b86-12bb97331649.png

我们需要一个真实的调试手段来验证我们的想法,这个调试手段就是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映射:

188ac456-4e4d-11eb-8b86-12bb97331649.png

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

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

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

18d0ee68-4e4d-11eb-8b86-12bb97331649.png

191e8b00-4e4d-11eb-8b86-12bb97331649.png

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

197a2640-4e4d-11eb-8b86-12bb97331649.png

我在内核启动参数加的rodata=0实际上是让rodata_full为false。如果我把这个kernel启动选项去掉,我得到的内核页表是完全不一样,线性映射区也全部是PTE映射:

199d2ee2-4e4d-11eb-8b86-12bb97331649.png

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

1a0676fe-4e4d-11eb-8b86-12bb97331649.png

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

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

1a6a8f18-4e4d-11eb-8b86-12bb97331649.png

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

1b367c4a-4e4d-11eb-8b86-12bb97331649.png

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

责任编辑:xj

原文标题:宋宝华: ARM64 Linux内核页表的块映射

文章出处:【微信公众号:Linuxer】欢迎添加关注!文章转载请注明出处。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • ARM
    ARM
    +关注

    关注

    135

    文章

    9583

    浏览量

    393507
  • 内核
    +关注

    关注

    4

    文章

    1474

    浏览量

    43089
  • Linux
    +关注

    关注

    88

    文章

    11810

    浏览量

    219513
  • 板块
    +关注

    关注

    0

    文章

    4

    浏览量

    7698

原文标题:宋宝华: ARM64 Linux内核页表的块映射

文章出处:【微信号:LinuxDev,微信公众号:Linux阅码场】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    Linux Kernel 6.1 tools目录全解析 | RK平台ARM64交叉编译实战指南

      在瑞芯微( RK ) ARM64 平台嵌入式 Linux 开发、内核调试与硬件外设测试中, Linux 内核源码自带的 tools 目录
    的头像 发表于 04-16 18:42 5495次阅读
    <b class='flag-5'>Linux</b> Kernel 6.1 tools目录全解析 | RK平台<b class='flag-5'>ARM64</b>交叉编译实战指南

    无法在 VF2 上运行 aarch64 / x86_64 容器怎么处理?

    设置好 qemu 和 podman 后,我尝试使用 --arch 运行跨架构容器,但遇到错误: user@starfive:~$ podman run --arch arm64
    发表于 03-17 06:27

    Linux内核驱动开发的技术核心精要

    嵌入式Linux驱动开发是连接硬件与操作系统的关键环节。随着内核演进(如Linux 6.13)和硬件复杂度提升,开发者需掌握并发控制、中断分层、内存管理、设备树、调试工具等核心知识。本文提炼出驱动
    发表于 03-10 13:56

    从架构到驱动:这三本经典书,承包了我的嵌入式Linux入门与进阶

    作为一个深耕嵌入式领域的开发者,书架上总有几本“压箱底” 的书 —— 它们既是新手入门的灯塔,也是老手复盘的手册。今天想和大家聊聊几本经典的书,《ARM64 体系结构编程与实践》《鸟哥的 Linux
    的头像 发表于 02-09 17:02 1335次阅读

    深入剖析ARM64内核关键文件:kernel-6.1/arch/arm64/kernel/head.S

    ARM64 架构的 Linux 内核开发中,arch/arm64/kernel/head.S是一个绕不开的关键文件—— 它是内核启动早期
    的头像 发表于 02-04 17:35 1370次阅读
    深入剖析<b class='flag-5'>ARM64</b><b class='flag-5'>内核</b>关键文件:kernel-6.1/arch/<b class='flag-5'>arm64</b>/kernel/head.S

    深入RK3588内核:rockchip_linux_defconfig的作用与调试价值

    在 RK3588 芯片的 Linux 开发中,有一个文件始终是开发者绕不开的核心 ——kernel/arch/arm64/configs/rockchip_linux_defconfig。无论是首次
    的头像 发表于 02-03 15:56 1358次阅读
    深入RK3588<b class='flag-5'>内核</b>:rockchip_<b class='flag-5'>linux</b>_defconfig的作用与调试价值

    调试RK3588必看!这个临时设备树文件,藏着你要的所有配置答案

    做RK3588 芯片开发的小伙伴,肯定在kernel/arch/arm64/boot/dts/rockchip/目录下见过一个“长名字文件
    的头像 发表于 02-03 15:55 1416次阅读
    调试RK3588必看!这个临时设备树文件,藏着你要的所有配置答案

    【「Linux 设备驱动开发(第 2 版)」阅读体验】+读深入理解Linux内核内存分配

    ,目前4KB是广泛使用的大小。在Linux操作系统中,每个进程甚至内核本身都被分配了地址空间,这是处理器的虚拟地址空间的一部分,内核和进程都不处理物理地址,物理地址由MMU处理。 虚
    发表于 01-16 20:05

    深入剖析ARM64异常处理:开发者必须掌握的底层核心逻辑

    ARM64架构的开发领域,异常处理绝非单纯的理论知识点,而是直接决定系统稳定性、调试效率和功能实现的关键技术。无论是嵌入式开发、Linux内核移植,还是驱动开发与芯片调试,理解异常发生后CPU
    的头像 发表于 12-24 07:05 1322次阅读
    深入剖析<b class='flag-5'>ARM64</b>异常处理:开发者必须掌握的底层核心逻辑

    Linux内核模块的加载机制

    \"GPL\") bool sig_ok;// 签名验证结果 };然后进行内存分配 1、使用vmalloc()在内核空间分配内存,映射模块的代码和数据段。 2、标记可执行(需
    发表于 11-25 06:59

    【米尔NXP i.MX 91开发板评测】移植和运行RT-Linux,实时性能测试

    CONFIG_PREEMPT_RT make olddefconfig echo \"编译内核和模块...\" make -j$(nproc) ARCH=arm64
    发表于 09-01 10:11

    【HZ-RK3568开发板免费体验】HZ-RK3568开发板操作系统编译

    RK_KERNEL_CFG=rockchip_linux_defconfig RK_KERNEL_DTB=kernel/arch/arm64/boot/dts/rockchip
    发表于 08-17 09:15

    【HZ-RK3568开发板免费体验】3、开启Linux Kernel RT功能

    /aarch64-none-linux-gnu- make ARCH=arm64 rockchip_linux_defconfig rockchip_rt.config make -C /path/to/SDK/kernel
    发表于 07-22 14:03

    Windows Arm64托管运行器正式支持GitHub Actions

    过去一年,Arm 与 GitHub 持续紧密合作,致力于为基于 Arm 平台的开发者打造更便捷、更高效的开发体验。GitHub 推出的 Arm 托管运行器正在革新应用程序的开发与部署流程,而近期推出
    的头像 发表于 04-28 14:23 1281次阅读

    迅为RK3568开发板内核模块实现-编写 Makefile

    如下(图 4-5)所示: 代码解释如下: 第 1 行设置 ARCH 变量为 arm64 第 2 行设置交叉编译器前缀为 aarch64-linux-gnu- 第 3 行 obj-m += &
    发表于 04-24 13:36