转自:https://blog.csdn.net/sunlei0625/article/details/59476987

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sunlei0625/article/details/59476987
首先我们基于平坦型物理内存,单个node,下面是基于64位ARMv8架构得到,其他架构也有类似结论: 首先我们知道在我们成功编译好kernel后会生成一个system.map文件,其给出了内核整个虚拟地址空间情况,比如:

ARM64: 整个内核空间起始地址: ffffffc000080000 T _text

代码段起始地址: ffffffc000080160 T stext

异常向量表地址: ffffffc000083000 T vectors

ffffffc0010890b8 B __bss_start ffffffc0010890b8 D _edata

ffffffc00191ab58 B mem_map ffffffc00191ab60 B max_mapnr

ffffffc001c7dd28 B __bss_stop ffffffc001c7e000 B idmap_pg_dir ffffffc001c80000 B swapper_pg_dir ffffffc001c82000 B _end

ARM:

c0003000 A swapper_pg_dir c0008000 T _text c0008000 T stext c0008090 t __create_page_tables c0008168 t __turn_mmu_on_loc c0008174 T secondary_startup c00081e0 T __secondary_switched c00081ec t __secondary_data c00081f8 t __enable_mmu c0008220 t __vet_atags c0008280 T __exception_text_start c0008280 T _stext c0008280 T asm_do_IRQ c0008284 T do_undefinstr

c0caccb8 b suspend_time c0caccc0 b last_transmit c0caccc8 b activity_lock c0caccd0 b klist_remove_lock c0caccd4 B __bss_stop c0caccd4 B _end

对于ARM来说,32位和64位明显不同。

对整个内核空间代码和数据来说,由于他们是直接映射的,我们这里讨论起来非常简单

对于这些地址,内核通过宏__pa()找到这些虚拟地址对应的物理地址。或者通过__va()找到物理地址对应

的虚拟地址。

arch/arm64/include/asm/memory.h #define __pa(x)   __virt_to_phys((unsigned long)(x)) #define __va(x)   ((void *)__phys_to_virt((phys_addr_t)(x))) #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) #define virt_to_pfn(x)      __phys_to_pfn(__virt_to_phys(x))

继续看定义:

include/asm-generic/memory-module.h #define __virt_to_phys(x) (((phys_addr_t)(x) - PAGE_OFFSET + PHYS_OFFSET)) #define __phys_to_virt(x) ((unsigned long)((x) - PHYS_OFFSET + PAGE_OFFSET))

#define __phys_to_pfn(paddr) ((unsigned long)((paddr) >> PAGE_SHIFT)) #define __pfn_to_phys(pfn) ((phys_addr_t)(pfn) << PAGE_SHIFT)

#define __pfn_to_page(pfn) (mem_map + ((pfn) - ARCH_PFN_OFFSET)) #define __page_to_pfn(page) ((unsigned long)((page) - mem_map) + ARCH_PFN_OFFSET)

这里需要注意PAGE_OFFSET和PHYS_OFFSET定义,前者是整个内核空间开始的虚拟地址,一般跟体系结构相关,

如经典32为X86和ARM为0xC0000000,即3GB处。对于64位来说,一般为0xFFFFFFC000000000 而PHYS_OFFSET则为物理内存起始地址,一般来说,这个偏移跟芯片设计相关,这个偏移表示了访问DDR最低的地址线。

比如说,如果PHYS_OFFSET为1GB,DDR大小为2GB,那么有效访问DDR空间的地址必须是

0x40000000--0xC0000000之间。

在我们上面给出的system.map中的虚拟地址可以看到, 内核虚拟地址起始地址起始为0xFFFFFFC000000000,这个由内核定义的PAGE_OFFSET给出。 而内核真正开始的使用的地址为0xffffffc000080000这里有个偏移量,大小为128个页(4KB)。总大小为512kB字节。

在本实验上测试,得到PHYS_OFFSET为0x8800000,即136MB开始处。

可以看到,一个内核中的虚拟地址,即0xFFFFFFC000000000以上的地址,如果寻找其物理地址,使用__pa()宏非常简单,

比如上面的mem_map的虚拟地址为0xffffffc00191ab58,其减去起始虚拟地址0xFFFFFFC000000000后为0x0191ab58,

然后根据物理地址偏移情况,得到真正的物理地址:0x0191ab58 + PAGE_OFFSET = 0x0191ab58 + 0x8800000

= 0xA11AB58 可以看到,mem_map的物理地址为0xA11AB58。 那么对于这个物理地址,对应的页帧号为多少呢?即PFN为多少,这时需要使用上面的__phys_to_pfn(). 可以看到,直接进行右移即可。例如对于上面的mem_map来说,其页帧为0xA11A。

那么对于一个PFN,其对应的物理page描述符是多少呢?这时需要使用宏__pfn_to_page(),注意这里使用的 ARCH_PFN_OFFSET,其就是偏移PHYS_OFFSET对应的页帧号,上面为0x8800000则对应页帧为0x8800,则上面mem_map对应的页帧号 为0xA11A -  0x8800 = 0x191A ,然后再根据mem_map数组,即 mem_map[0x191A]为mem_map的页描述符。
————————————————
版权声明:本文为CSDN博主「星空探索」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sunlei0625/article/details/59476987

Linux 内核虚拟地址到物理地址转换讨论【转】的更多相关文章

  1. Linux驱动虚拟地址和物理地址的映射

    一般情况下,Linux系统中,进程的4GB内存空间被划分成为两个部分------用户空间和内核空间,大小分别为0~3G,3~4G. 用户进程通常情况下,只能访问用户空间的虚拟地址,不能访问到内核空间. ...

  2. linux内核——PAE(物理地址扩展)

    引入PAE机制后,分页模式是怎样的呢? 首先,要搞明白几件事,2.6.11以上版本的linux内核中,存在4中页表(页全局目录,页上级目录,页中级目录,页表),这些页表结构是已经存在于硬盘中的,当进程 ...

  3. Linux内核源代码的学习过程转换完成细节

    linux中的进程是个最主要的概念,进程从执行队列到開始执行有两个開始的地方, 一个就是switch_to宏中的标号1:"1:/t",//仅仅要不是新创建的进程,差点儿都是从上面的 ...

  4. linux内核里的字符串转换 ,链表操作常用函数(转)

    1.对双向链表的具体操作如下: list_add ———向链表添加一个条目 list_add_tail ———添加一个条目到链表尾部 __list_del_entry ———从链表中删除相应的条目 l ...

  5. Linux内核中ioremap映射的透彻理解

    几乎每一种外设都是通过读写设备上的寄存器来进行的,通常包括控制寄存器.状态寄存器和数据寄存器三大类,外设的寄存器通常被连续地编址.根据CPU体系结构的不同,CPU对IO端口的编址方式有两种: (1)I ...

  6. Linux内核开发基础

    1.Linux内核简介 1.1.Linux系统如何构成 内核空间(Kernel Space)+用户空间(User Space) 用户空间 = 用户程序 + C语言库(例如:GNC C Library) ...

  7. linux内核申请内存函数

    kmap函数:    把某块高端内存映射到页表,然后返回给用户一个填好vitual字段的page结构    建立永久地址映射,不是简单的返回virtual字段的pageioremap:    驱动程序 ...

  8. 经典]Linux内核中ioremap映射的透彻理解【转】

    转自:http://blog.csdn.net/lanyang123456/article/details/7403514 几乎每一种外设都是通过读写设备上的寄存器来进行的,通常包括控制寄存器.状态寄 ...

  9. (笔记)Linux内核中ioremap映射的透彻理解

    几乎每一种外设都是通过读写设备上的寄存器来进行的,通常包括控制寄存器.状态寄存器和数据寄存器三大类,外设的寄存器通常被连续地编址.根据CPU体系结构的不同,CPU对IO端口的编址方式有两种: (1)I ...

随机推荐

  1. ruby中的数组相关方法介绍

    l = ["a","b","c","d","e","f",'g'] puts l ...

  2. RocketMq在SparkStreaming中的应用总结

    其实Rocketmq的给第三方的插件已经全了,如果大家有兴趣的话请移步https://github.com/apache/rocketmq-externals.本文主要是结合笔者已有的rmq在spar ...

  3. FCC---CSS Flexbox: Apply the flex-direction Property to Create Rows in the Tweet Embed

    The header and footer in the tweet embed example have child items that could be arranged as rows usi ...

  4. Cesium专栏-填挖方分析(附源码下载)

    Cesium 是一款面向三维地球和地图的,世界级的JavaScript开源产品.它提供了基于JavaScript语言的开发包,方便用户快速搭建一款零插件的虚拟地球Web应用,并在性能,精度,渲染质量以 ...

  5. 【Cocos谁学谁会】定制属于自己的脚本模板

    版权申明: 本文原创首发于以下网站,您可以自由转载,但必须加入完整的版权声明 博客园:https://www.cnblogs.com/MogooStudio/ csdn博客:https://blog. ...

  6. Python基础语法-List

    列表的操作方法 列表中存放的数据是可以进行修改的,比如"增"."删"."改"" 添加元素("增" append ...

  7. SQL Server 之事务执行,让语句在事务中执行

    BEGIN TRAN     BEGIN  TRY DELETE FROM dbo.表 INSERT INTO  dbo.表(    Id,   字段....) SELECTId,字段...    F ...

  8. 阿里云RDS for SQL Serrver关于权限的一个严重Bug

    阿里云RDS for SQL Server的账号管理有不少小Bug,而且有一个很严重的Bug:任何普通账号,都能创建数据库.注意,我这里是说任意普通账号,任意任意普通账号!任意任意普通账号!重要的事情 ...

  9. 高维数据Lasso思路

    海量数据的特征工程中, 如果数据特征维度达到几千乃至上万 常规的lasso很容易失效 这里介绍几种泛义lasso,是在实际数据处理中常用的 迭代与分块思路/分组的使用(有兴趣的同学可自行实践一下) 1 ...

  10. appium---模拟点击事件

    在做自动化的过程中都会遇到一些无法定位到的地方,或者通过元素怎么都定位不成功的地方,这个时候我们可以使用必杀技,通过坐标定位.具体的怎么操作呢? swipe点击事件 前面安静写过一篇关于swipe的滑 ...