初学者学习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. MyBatis Plus 2.3 个人笔记-02-基本注解

    实体类注解 /* * MybatisPlus会默认使用实体类的类名到数据中找对应的表. * */ @TableName("tbl_employee") public class E ...

  2. 【动态规划】洛谷P1802 5 倍经验日(01背包问题)

    一个洛谷普及-的题目,也是我刚刚入门学习动态规划的练习题. 下面发一下我的思路和代码题解: 我的思路及伪代码: 我的AC图: 接下来上代码: 1 //动态规划 洛谷P1802 五倍经验日 2 #inc ...

  3. (原创)[C#] 一步一步自定义拖拽(Drag&Drop)时的鼠标效果:(一)基本原理及基本实现

    一.前言 拖拽(Drag&Drop),属于是极其常用的基础功能. 无论是在系统上.应用上.还是在网页上,拖拽随处可见.同时拖拽时的鼠标效果也很漂亮,像这样: 这样: 还有这样: 等等等等. 这 ...

  4. 顺利通过EMC实验(1)

  5. 前端学习02:jQuery 日历

    引言:学习前端已经接近1个月了,先后经历了1周的 html+css, 2周的"JavaScript 从入门到下跪",期间还看了vue+webpack.然鹅,Mentor Brigh ...

  6. JDBC/Mybatis连接数据库报错:The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone.

    造成这个的原因是maven导入MyBatis的时候会自动导入最新版本的8.0.11,然后8.0.11采用了新驱动,之前版本会报错. 当我们使用高版本的MySQL驱动时可以在获取数据库的连接getCon ...

  7. Java中的反射原理以及简单运用(原理+例子)

    @ 目录 学习总结 1. 为什么要使用反射 2. 反射的概念 3. Java反射加载过程 4. 反射优缺点 5. 字节码对象理解 6. 获取字节码对象(.class)的三种方式 7. 反射常用API ...

  8. EMS查看单个邮箱的已用空间

    Exchange查询单个邮箱用户使用状态. 查看用户"wsj"的邮箱使用状态 键入以下命令: Get-MailboxStatistics wsj | FL DiplayName,T ...

  9. MySQL安装配置教程(超级详细)

    一. 下载MySQL Mysql官网下载地址:https://downloads.mysql.com/archives/installer/ 1. 选择要安装的版本,本篇文章选择的是5.7.31版本, ...

  10. Java学习day6

    今天跟着教学视频做了个简易的学生管理系统 在编写完全部代码之后出现了在空白处右键没有run as选项的问题,通过csdn与博客园上的多个帖子介绍,得知是jdk配置不对,正确配置后问题得到解决 明天学习 ...