用crash tool观察ARM64 Linux地址转换
初学者学习Linux系统地址转换时,如果只是学习理论,又或者研读代码,那可能感觉比较枯燥。此时如果可以利用某些工具实际观察一下地址转换的过程,那可能会给枯燥的内核学习带来些微的乐趣。crash tool是一款内核调试工具,常用来分析内核崩溃问题。我们可以手动触发内核崩溃,然后借用该工具来分析当时系统的运行情况,当然也包括内存的运行情况。
本文基于ARMv8 AArch64 (简称ARM64)架构,Linux 4.14内核来讲述。首先回顾一下内存访问的相关知识点。
1、ARM内存访问的硬件架构
ARM有MMU部件,现代操作系统一般都会启用MMU来访问内存。启用MMU之后,多进程就有了可能,每个进程可以维护各自私有的虚拟地址空间,无需关心物理内存布局。
2、虚拟地址空间到物理地址空间的映射
虚拟地址到物理地址的映射是通过查表的机制来实现,下图是一种典型的地址映射布局。内核空间地址的高16位(bit[63:48])为全1,其转换表的基地址存放在TTBR1_EL1寄存器中;用户空间地址的高16位(bit[63:48])为全0,其转换表的基地址存放在TTBR0_EL0寄存器中。
除了高16位外,剩下的48位,也并不全用作虚拟地址空间,使用多少位是可以配置的,比如Linux系统的内核一般做如下配置,表示有39位虚拟地址空间。
CONFIG_ARM64_VA_BITS=39
39位虚拟地址空间,内核空间范围为0xFFFFFF80_00000000 ~ 0xFFFFFFFF_FFFFFFFF,用户空间范围为0x00000000_00000000 ~ 0x0000007F_FFFFFFFF。
3、转换表的格式
转换表有4个级别,level 0 ~ level 3。
如下图所示,当bit[1:0]为2b'11时,表示该表项是Table descriptor,指向下一级转换表的地址。而当 bit[1:0]为2b'01时,表示Block entry,不指向下一级转换表,而是直接输出block address。当表项处于level 3时,即使bit[1:0]为2b'11,也不再指向下一级转换表,而是输出block address。
4、地址转换的过程
如下图所示,以39位虚拟地址为例,来了解地址转换过程。图片是取自ARMv8官方文档,建议放大了看。
内存中维护着三个转换表,从虚拟地址转换成物理地址,要经过三次查表的过程。
39位虚拟地址被分成了4部分,作用如下:
bit[38:30] —— 索引第一级表中的表项
bit[29:21] —— 索引第二级表中的表项
bit[20:12] —— 索引第三级表中的表项
bit[11:0] —— 页内偏移
第一步,TTBR寄存器中存放了第一级转换表的起始地址,虚拟地址的bit[38:30]的值表示要查找转换表中的第几项,这个值左移三位(64位地址,每个表项占用8字节),再加上第一级转换表的地址,就是该表项的地址。该表项存放的是第二级转换表的起始地址。
第二步,用第二级转换表的起始地址,再结合虚拟地址的bit[29:21],与第一步计算方法类似,可以计算出第二级表项的地址。第二级表项中存放了第三级转换表的起始地址。
第三步,用第三级转换表的起始地址,再结合虚拟地址的bit[20:12],与第一步计算方法类似,可以计算出第三级表项的地址。第三级表项存放的是最终的页面描述符,有页面的物理地址信息,用这个地址再加上虚拟地址的bit[11:0],就是该虚拟地址对应的物理地址。
5、Linux内核中的关键数据结构
mm_struct结构体是内存描述符,内核用它来维护一个进程的地址空间的所有信息。这个结构体中包含了一个重要成员:pgd指针,pgd的名称是页全局目录,指向的是第一级转换表的的起始地址。
每个进程的task_struct结构体中,都包含了内存描述符。
init_mm全局变量,是内核本身的内存描述符。
有了以上知识点做支撑后,就可以用crash tool来验证自己的理解了。
6、用crash tool观察地址转换
手动触发内核崩溃的shell指令是:
echo "c" > /proc/sysrq-trigger
crash tool使用示例如下:
crash_arm64 vmlinux dumpfile -m phys_offset=0x80000000
进入crash tool环境后,我们选择1号进程,也就是init进程来分析。首先用bt命令看一下1号进程当前的调用栈。
我们选择该进程TASK的地址和SP寄存器指向的地址来进行实际分析。TASK的高位地址全为1,为内核空间的虚拟地址;SP地址高位全为0,为用户空间的地址。
先分析用户空间的虚拟地址 0000007feeac5cb0,将它分解如下:
bit[38:30] 0x1ff ,左移三位是 0xff8
bit[29:21] 0x175,左移三位是 0xba8
bit[20:12] 0x0c5,左移三位是 0x628
bit[11:0] 0xcb0
怎么找到它对应的物理地址呢?首先要找到第一级转换表所在的位置,也就是pgd的位置。我们在前面提到过,进程的内存描述中有pgd指针。所以我们可以先通过crash tool的task命令查看内存描述符的地址,再通过struct命令查看内存描述符中pgd指针的值。
知道了第一级转换表所在的位置,结合虚拟地址的bit[38:30],就可以算出虚拟地址在第一级转换表中所对应的表项位置:0xffffffc01bf60000 + 0xff8 = 0xffffffc01bf60ff8。用rd命令可以读取这个表项的值,这个值里面含有第二级转换表的起始地址信息。
读出的值是99c00003,bit[1:0]=2b'11,表示该表项类型为Table descriptor,指向下一级(第二级)转换表,起始地址是99c00000。结合虚拟地址的bit[29:21],可以算出虚拟地址在第二级转换表中的表项地址:0x99c00000 + 0xba8 = 0x99c00ba8。该地址是物理地址,需要用rd -p命令读取其值,这个值里面含有第三级转换表的起始地址信息。
读出的值是99c04003,表项类型也为Table descriptor。结合虚拟地址的bit[20:12],可以算出虚拟地址在第三级转换表中对应的表项地址:0x99c04000 + 0x628 = 0x99c04628。三级表项存放的是页面描述符信息,不再指向下一级转换表。
从0x99c04628地址读出的值是00e800008594bf53,结合第4节的地址转换过程图,bit[47:12]存放的是地址信息,与虚拟地址的bit[11:0](页内偏移)结合后,就构成了实际的物理地址:0x8594b000 + 0xcb0 = 0x8594bcb0。这个地址就是0x0000007feeac5cb0所对应的物理地址。
上述过程我们手动计算出了物理地址,那如何知道有没有算对呢?其实crash tool提供了vtop这个命令,可以直接显示虚拟地址到物理地址的转换结果,如下图所示。可以看到vtop命令的结果和我们手动计算的结果一致,说明我们对转换过程的理解是正确的。
再来分析内核空间的虚拟地址 ffffffc01bf60000,将它分解如下:
bit[38:30] 0x100,左移三位是 0x800
bit[29:21] 0x0df,左移三位是 0x6f8
bit[20:12] 0x160,左移三位是 0xb00
bit[11:0] 0x000
首先我们要知道内核空间的 pgd,结合前面第4节讲的Linux关键数据结构,内核空间的 pgd 是保存在 init_mm 变量中,用p命令打印变量结果,可以看到pgd指针的值。
每一级表项的计算过程与用户空间例子类似,可以得到如下观察结果:
注意,第二级表项的值是00f800008be00f11,bit[1:0]=2b'01,表示Block entry,不再指向下一级转换表,而是指示物理地址,地址值为9be00000。
两级转换表对应的是虚拟地址的bit[38:30]和bit[29:21],又没有第三级转换表,因此剩下的bit[20:0]都是页内偏移。所以计算出最终物理地址为:0x9be00000 + 0x160000 = 0x9bf60000。也就是说, ffffffc01bf60000的物理地址是9bf60000。
用vtop命令验证,和手动计算的结果一致。
------ END ------
作者:bigfish99
博客:https://www.cnblogs.com/bigfish0506/
公众号:大鱼嵌入式
用crash tool观察ARM64 Linux地址转换的更多相关文章
- Linux 网络编程详解一(IP套接字结构体、网络字节序,地址转换函数)
IPv4套接字地址结构 struct sockaddr_in { uint8_t sinlen;(4个字节) sa_family_t sin_family;(4个字节) in_port_t sin_p ...
- 操作系统-存储管理(5)IA-32/Linux的地址转换
IA-32/Linux按字节编址:在保护模式下,IA-32采用段页式虚拟存储管理方式,存储地址采用逻辑地址.线性地址和物理地址来进行描述. 逻辑地址由48位组成,包含16位段选择符(高13位为段表项的 ...
- ARM中MMU地址转换理解
首先,我们要分清ARM CPU上的三个地址:虚拟地址(VA,Virtual Address).变换后的虚拟地址(MVA,Modified Virtual Address).物理地址(PA,Physic ...
- Linux网络地址转换分析
Linux网络地址转换分析 地址转换用来改变源/目的端口,是netfilter的一部分,也是通过hook点上注册相应的结构来工作. Nat注册的hook点和conntrack相同,只是优先级不同,数据 ...
- OpenRisc-31-关于在设计具有DMA功能的ipcore时的虚实地址转换问题的分析与解决
引言 之前,我们在讨论基于ORPSoC的ipcore设计时提到过DMA的问题,当时我们实现DMA的功能时,访问的是local memory,并没有使用主存(即外部的SDRAM),使用的是本地的一块存储 ...
- UNIX网络编程——socket概述和字节序、地址转换函数
一.什么是socket socket可以看成是用户进程与内核网络协议栈的编程接口.socket不仅可以用于本机的进程间通信,还可以用于网络上不同主机的进程间通信. socket API是一层抽象的网络 ...
- 套接字编程相关函数(1:套接字地址结构、字节序转换、IP地址转换)
1. 套接字地址结构 1.1 IPv4套接字地址结构 IPv4套接字地址结构通常也称为“网际套接字地址结构”,它以sockaddr_in命名,定义在<netinet/in.h>头文件中.下 ...
- iptables实现网络防火墙及地址转换
iptables主机防火墙功能及常用命令 FSM:Finite State Machine 有限状态机 客户端:closed -->syn_sent -->established --&g ...
- Memory Translation and Segmentation.内存地址转换与分段
原文标题:Memory Translation and Segmentation 原文地址:http://duartes.org/gustavo/blog/ [注:本人水平有限,只好挑一些国外高手的精 ...
随机推荐
- ACM中的位运算技巧
听说位运算挺好玩的,那这节总结一下ACM中可能用到的位运算技巧. XOR运算极为重要!!(过[LC136](只出现一次的数字 - 力扣(LeetCode)):数组中每个数字都出现两次,只有一个出现一次 ...
- Docker最常用的镜像命令和容器命令
一.镜像相关命令 官方文档:https://docs.docker.com/referenc 1.1查看镜像 [root@localhost ~]# docker images REPOSITORY ...
- 1_开环系统和闭环系统_反馈控制_Open/Closed Loop System_Feedback
- JS练习实例--编写经典小游戏俄罗斯方块
最近在学习JavaScript,想编一些实例练练手,之前编了个贪吃蛇,但是实现时没有注意使用面向对象的思想,实现起来也比较简单所以就不总结了,今天就总结下俄罗斯方块小游戏的思路和实现吧(需要下载代码也 ...
- 王下邀月熊_Chevalier的前端每周清单系列文章索引
感谢 王下邀月熊_Chevalier 分享的前端每周清单,为方便大家阅读,特整理一份索引. 王下邀月熊大大也于 2018 年 3 月 31 日整理了自己的前端每周清单系列,并以年/月为单位进行分类,具 ...
- c++实现中介者模式--虚拟聊天室
内容: 在"虚拟聊天室"实例中增加一个新的具体聊天室类和一个新的具体会员类,要求如下: 1. 新的具体聊天室中发送的图片大小不得超过20M. 2. 新的具体聊天室中发送的文字长度不 ...
- mapreduce分区
本次分区是采用项目垃圾分类的csv文件,按照小于4的分为一个文件,大于等于4的分为一个文件 源代码: PartitionMapper.java: package cn.idcast.partition ...
- Idea中创建maven项目(超详细)
Idea中创建maven项目 提示:前提条件时maven已经安装好,并且环境变量也配置完成,maven没安装好或者环境变量没有配置好的请参考我上一篇文章--maven的安装和配置 上篇博文链接:htt ...
- CSS简单样式练习(六)
运行效果: 源代码: 1 <!DOCTYPE html> 2 <html lang="zh"> 3 <head> 4 <meta char ...
- vue中执行npm run build报错解决方法?
遇到了执行npm run build 后报错: [build:js ] Module not found: Error: Can't resolve 'scss-loader' in 'D:\work ...