转自: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. webpack-dev-server工具

    webpack-dev-server来实现自动打包编译功能 // 1.npm install webpack-dev-server -D//2.和webpakc命令用法一样// 3.是本地安装的,无法 ...

  2. 带你揭秘Shiro(一)

    提到Shiro,不得不先介绍RBAC介绍 RBAC介绍: RBAC是基于角色的访问控制(Role-Based Access Control )在 RBAC 中,权限与角色相关联,用户通过成为适当角色的 ...

  3. Java生鲜电商平台-电商会员体系搭建

    Java生鲜电商平台-电商会员体系搭建 说明:因为之前一直从事的是B端的生鲜电商方面的产品,对会员体系方面有深刻的理解,今天来聊一聊会员体系的搭建. 明确会员体系的目的 首先我们需要明确的知道,搭建电 ...

  4. Java 基础复习 -- Enum 类

    一.枚举类基本语法 在 Java SE5 中添加了一个看似很小的特性,即 enum 关键字,它使得我们在需要群组并使用枚举类型集时,可以很方便的处理. 所有的 enum 都继承自 java.lang. ...

  5. 华为开发者联盟 方舟编译器 DevEco IDE

    华为开发者联盟 https://developer.huawei.com/consumer/cn/ 方舟编译器是为支持多种编程语言.多种芯片平台的联合编译.运行而设计的统一编程平台,包含编译器.工具链 ...

  6. 使用 Flutter 开发 Mac 桌面应用

    Flutter 可以开发 Mac,Linux,Windows 桌面,但是对于平台目前只能打对于的包,以及调试本平台的包. 切换到 master 分支 首先必须切换到 master 分支.我之前在 de ...

  7. stdc++.6.0.9动态库缺失

    问题 ld: library not found for -lstdc++.6.0.9 clang: error: linker command failed with exit code 1 (us ...

  8. Android 弹出Dialog时隐藏状态栏和底部导航栏

    上代码 dialog.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); di ...

  9. CODING 2.0:为什么我们需要 DevOps

    CODING 在前两天的 Kubecon 2019 大会上发布了 CODING 2.0.这背后是 CODING 对研发管理和研发团队组建的思考.从 CODING 成立以来,我们一直在探索"让 ...

  10. MySQL 学习笔记 (一)

    1.InnoDB and Online DDL ALTER TABLE tbl_name ADD PRIMARY KEY (column), ALGORITHM=INPLACE, LOCK=NONE; ...