初学者学习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地址转换的更多相关文章

  1. Linux 网络编程详解一(IP套接字结构体、网络字节序,地址转换函数)

    IPv4套接字地址结构 struct sockaddr_in { uint8_t sinlen;(4个字节) sa_family_t sin_family;(4个字节) in_port_t sin_p ...

  2. 操作系统-存储管理(5)IA-32/Linux的地址转换

    IA-32/Linux按字节编址:在保护模式下,IA-32采用段页式虚拟存储管理方式,存储地址采用逻辑地址.线性地址和物理地址来进行描述. 逻辑地址由48位组成,包含16位段选择符(高13位为段表项的 ...

  3. ARM中MMU地址转换理解

    首先,我们要分清ARM CPU上的三个地址:虚拟地址(VA,Virtual Address).变换后的虚拟地址(MVA,Modified Virtual Address).物理地址(PA,Physic ...

  4. Linux网络地址转换分析

    Linux网络地址转换分析 地址转换用来改变源/目的端口,是netfilter的一部分,也是通过hook点上注册相应的结构来工作. Nat注册的hook点和conntrack相同,只是优先级不同,数据 ...

  5. OpenRisc-31-关于在设计具有DMA功能的ipcore时的虚实地址转换问题的分析与解决

    引言 之前,我们在讨论基于ORPSoC的ipcore设计时提到过DMA的问题,当时我们实现DMA的功能时,访问的是local memory,并没有使用主存(即外部的SDRAM),使用的是本地的一块存储 ...

  6. UNIX网络编程——socket概述和字节序、地址转换函数

    一.什么是socket socket可以看成是用户进程与内核网络协议栈的编程接口.socket不仅可以用于本机的进程间通信,还可以用于网络上不同主机的进程间通信. socket API是一层抽象的网络 ...

  7. 套接字编程相关函数(1:套接字地址结构、字节序转换、IP地址转换)

    1. 套接字地址结构 1.1 IPv4套接字地址结构 IPv4套接字地址结构通常也称为“网际套接字地址结构”,它以sockaddr_in命名,定义在<netinet/in.h>头文件中.下 ...

  8. iptables实现网络防火墙及地址转换

    iptables主机防火墙功能及常用命令 FSM:Finite State Machine 有限状态机 客户端:closed -->syn_sent -->established --&g ...

  9. Memory Translation and Segmentation.内存地址转换与分段

    原文标题:Memory Translation and Segmentation 原文地址:http://duartes.org/gustavo/blog/ [注:本人水平有限,只好挑一些国外高手的精 ...

随机推荐

  1. 爬虫-数据解析-bs4

    1.数据解析 解析: 根据指定的规则对数据进行提取 作用: 实现聚焦爬虫 数据解析方式: - 正则表达式 - bs4 - xpath 数据解析的通用原理: 数据解析需要作用在页面源码中(一组html标 ...

  2. 谷歌浏览器postman插件安装,亲测可用

    将谷歌浏览器进入扩展程序,将crx文件拖入即可. https://pan.baidu.com/s/1rIEe9RSby5EgTkygSx_dDA 百度云链接: https://pan.baidu.co ...

  3. 滑动窗口法——Leetcode例题

    滑动窗口法--Leetcode例题(连更未完结) 1. 方法简介 滑动窗口法可以理解为一种特殊的双指针法,通常用来解决数组和字符串连续几个元素满足特殊性质问题(对于字符串来说就是子串).滑动窗口法的显 ...

  4. PCB布线总的原则

    转自张飞实战电子公众号 PCB布线总的原则 最短路径和减少干扰 PCB布线的总的流程大致如下: 1了解制造厂商的制造规范-线宽,线间距,过孔要求及层数要求: 2确定层数并定义各层的功能: 3设计布线规 ...

  5. 前端进阶(12) - css 的弱化与 js 的强化

    css 的弱化与 js 的强化 web 的三要素 html, css, js 在前端组件化的过程中,比如 react.vue 等组件化框架的运用,使 html 的弱化与 js 的强化 成为了一种趋势, ...

  6. Django ElasticSearch Ionic 打造 GIS 移动应用 —— 架构设计

    搜索引擎是个好东西,GIS也是个好东西.当前还有Django和Ionic.最后效果图 构架设计 对我们的需求进行简要的思考后,设计出了下面的一些简单的架构. GIS架构说明 -- 服务端 简单说明: ...

  7. java如何读取和遍历properties文件

    在java项目开发过程中,使用properties文件作为配置基本上是必不可少的,很多如系统配置信息,文件上传配置信息等等都是以这种方式进行保存.同时学会操作properties文件也是java基础. ...

  8. Restful-API和传统API的对比

    阮一峰 RestFul-API 详解链接:  http://www.ruanyifeng.com/blog/2014/05/restful_api.html 举例,传统api设计: 举例,RestFu ...

  9. 定时-TimerTask

    /** * @param args * @throws InterruptedException */ public static void main(String[] args) throws In ...

  10. Blazor组件自做八 : 使用JS隔离封装屏幕键盘kioskboard.js组件

    1. 运行截图 演示地址 2. 在文件夹wwwroot/lib,添加kioskboard子文件夹,添加kioskboards.js文件 2.1 常规操作,懒加载js库, export function ...