初学者学习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. ACM中的位运算技巧

    听说位运算挺好玩的,那这节总结一下ACM中可能用到的位运算技巧. XOR运算极为重要!!(过[LC136](只出现一次的数字 - 力扣(LeetCode)):数组中每个数字都出现两次,只有一个出现一次 ...

  2. Docker最常用的镜像命令和容器命令

    一.镜像相关命令 官方文档:https://docs.docker.com/referenc 1.1查看镜像 [root@localhost ~]# docker images REPOSITORY ...

  3. 1_开环系统和闭环系统_反馈控制_Open/Closed Loop System_Feedback

  4. JS练习实例--编写经典小游戏俄罗斯方块

    最近在学习JavaScript,想编一些实例练练手,之前编了个贪吃蛇,但是实现时没有注意使用面向对象的思想,实现起来也比较简单所以就不总结了,今天就总结下俄罗斯方块小游戏的思路和实现吧(需要下载代码也 ...

  5. 王下邀月熊_Chevalier的前端每周清单系列文章索引

    感谢 王下邀月熊_Chevalier 分享的前端每周清单,为方便大家阅读,特整理一份索引. 王下邀月熊大大也于 2018 年 3 月 31 日整理了自己的前端每周清单系列,并以年/月为单位进行分类,具 ...

  6. c++实现中介者模式--虚拟聊天室

    内容: 在"虚拟聊天室"实例中增加一个新的具体聊天室类和一个新的具体会员类,要求如下: 1. 新的具体聊天室中发送的图片大小不得超过20M. 2. 新的具体聊天室中发送的文字长度不 ...

  7. mapreduce分区

    本次分区是采用项目垃圾分类的csv文件,按照小于4的分为一个文件,大于等于4的分为一个文件 源代码: PartitionMapper.java: package cn.idcast.partition ...

  8. Idea中创建maven项目(超详细)

    Idea中创建maven项目 提示:前提条件时maven已经安装好,并且环境变量也配置完成,maven没安装好或者环境变量没有配置好的请参考我上一篇文章--maven的安装和配置 上篇博文链接:htt ...

  9. CSS简单样式练习(六)

    运行效果: 源代码: 1 <!DOCTYPE html> 2 <html lang="zh"> 3 <head> 4 <meta char ...

  10. vue中执行npm run build报错解决方法?

    遇到了执行npm run build 后报错: [build:js ] Module not found: Error: Can't resolve 'scss-loader' in 'D:\work ...