当前位置: 首页 > news >正文

Linux 内核内存管理 page_address 函数

文章目录

  • 一、page_address
    • 1.1 page_address
    • 1.2 page_to_pfn
    • 1.3 PFN_PHYS
    • 1.4 __va(x)
    • 1.5 总结
    • 1.6 page_to_virt
  • 二、使用demo

一、page_address

1.1 page_address

内核用 struct page 结构体来表示系统中的每个物理页面,该结构体用来跟踪和管理这些物理页面的使用情况。

page_address函数根据给定的struct page 结构体返回该物理页面的内核起始虚拟地址:

// linux-3.10/include/linux/mm.hstatic __always_inline void *lowmem_page_address(const struct page *page)
{return __va(PFN_PHYS(page_to_pfn(page)));
}#if !defined(HASHED_PAGE_VIRTUAL) && !defined(WANT_PAGE_VIRTUAL)
#define page_address(page) lowmem_page_address(page)

对于内核高版本:

// linux-5.4.18/include/linux/mm.hstatic __always_inline void *lowmem_page_address(const struct page *page)
{return page_to_virt(page);
}#if !defined(HASHED_PAGE_VIRTUAL) && !defined(WANT_PAGE_VIRTUAL)
#define page_address(page) lowmem_page_address(page)

1.2 page_to_pfn

# cat /boot/config-3.10.0-1160.el7.x86_64 | grep CONFIG_SPARSEMEM_VMEMMAP
CONFIG_SPARSEMEM_VMEMMAP=y
//linux-3.10/include/asm-generic/memory_model.hif defined(CONFIG_SPARSEMEM_VMEMMAP)/* memmap is virtually contiguous.  */
#define __page_to_pfn(page)	(unsigned long)((page) - vmemmap)#define page_to_pfn __page_to_pfn

page_to_pfn宏根据给定struct page 结构体获取该物理页面的页帧号pfn。

1.3 PFN_PHYS

# cat /boot/config-3.10.0-1160.el7.x86_64 | grep CONFIG_PHYS_ADDR_T_64BIT
CONFIG_PHYS_ADDR_T_64BIT=y
#ifdef CONFIG_PHYS_ADDR_T_64BIT
typedef u64 phys_addr_t;
/* PAGE_SHIFT determines the page size */
#define PAGE_SHIFT	12#define PFN_PHYS(x)	((phys_addr_t)(x) << PAGE_SHIFT)

PFN_PHYS宏根据给定的物理页面的页帧号pfn获取其该物理页面的物理起始地址。

参数 x 是一个页框号(PFN),PAGE_SHIFT 是一个常量,表示页的大小的位移值(通常为 12,在 x86 架构上表示 4KB 大小的页面)。

这个宏通过将页框号左移 PAGE_SHIFT 位来计算物理地址。由于页框号是以页为单位计数的,每个页的大小为 2^PAGE_SHIFT 字节,所以将页框号左移 PAGE_SHIFT 位相当于将其乘以页的大小,得到物理地址。

1.4 __va(x)

(1)x86_64:
在x86_64等64位架构中,因为内核虚拟空间较大,所以直接把所有物理内存直接线性映射到内核虚拟地址当中,物理地址和内核虚拟地址仅仅一个PAGE_OFFSET的偏移:

#define __PAGE_OFFSET           _AC(0xffff880000000000, UL)#define PAGE_OFFSET		((unsigned long)__PAGE_OFFSET)// linux-3.10/arch/x86/include/asm/page.h
#define __va(x)			((void *)((unsigned long)(x)+PAGE_OFFSET))

__va() 宏用于将给定的物理地址转换为对应的内核虚拟地址。

这个宏将物理地址转换为虚拟地址的过程是将物理地址转换为无符号长整型,然后加上 PAGE_OFFSET 值。PAGE_OFFSET 是一个常量,表示内核虚拟地址的偏移量,是内核代码和数据在虚拟地址空间中的起始位置。

通过将物理地址加上 PAGE_OFFSET,就可以将物理地址转换为对应的虚拟地址。

备注:
内核虚拟地址空间都是直接映射区,地址连续,和物理地址空间是简单的线性映射关系。虽然内核虚拟地址空间是直接映射区,但还是会建立页表。

ffff880000000000 - ffffc7ffffffffff (=64 TB) direct mapping of all phys. memory

(2)arm64架构:

# cat /boot/config-5.4.18-74-generic | grep CONFIG_ARM64_VA_BITS
CONFIG_ARM64_VA_BITS_39=y
# CONFIG_ARM64_VA_BITS_48 is not set
CONFIG_ARM64_VA_BITS=39

CONFIG_ARM64_VA_BITS_39 是一个内核配置选项,用于指定 ARM64 架构中虚拟地址的位数。该选项设置为 y 表示启用 39 位虚拟地址。

ARM64 架构支持不同的虚拟地址位数配置,可以根据系统需求进行调整。虚拟地址位数决定了虚拟地址空间的大小。

其中,39 位配置是较为常见的配置,适用于大多数 ARM64 架构的系统。它提供了 512 GB 的虚拟地址空间。

// /arch/arm64/include/asm/memory.h/* PHYS_OFFSET - the physical address of the start of memory. */
#define PHYS_OFFSET		({ VM_BUG_ON(memstart_addr & 1); memstart_addr; })/** PAGE_OFFSET - the virtual address of the start of the linear map, at the*               start of the TTBR1 address space.* PAGE_END - the end of the linear map, where all other kernel mappings begin.* KIMAGE_VADDR - the virtual address of the start of the kernel image.* VA_BITS - the maximum number of bits for virtual addresses.*/
#define VA_BITS			(CONFIG_ARM64_VA_BITS)
#define _PAGE_OFFSET(va)	(-(UL(1) << (va)))
#define PAGE_OFFSET		(_PAGE_OFFSET(VA_BITS))
// linux-5.4.18/arch/arm64/mm/init.cvoid __init arm64_memblock_init(void)
{......physvirt_offset = PHYS_OFFSET - PAGE_OFFSET;......
}
// linux-5.4.18/arch/arm64/include/asm/memory.h#define __phys_to_virt(x)	((unsigned long)((x) - physvirt_offset))#define __va(x)			((void *)__phys_to_virt((phys_addr_t)(x)))

参数 x 是一个物理地址,physvirt_offset 是一个常量,表示物理地址和虚拟地址之间的偏移量。

这个宏通过将给定的物理地址减去 physvirt_offset 来计算对应的虚拟地址。偏移量 physvirt_offset 可能因架构和环境而异,它表示物理地址与虚拟地址之间的差距。

s64 physvirt_offset __ro_after_init;
EXPORT_SYMBOL(physvirt_offset);
# cat /proc/kallsyms | grep physvirt_offset
ffffffc01123d2e8 R physvirt_offset

1.5 总结

通过伙伴系统接口分配得到一个struct page以后,调用page_address函数过程:
(1)page_to_pfn宏根据给定struct page 结构体获取该物理页面的页帧号pfn。
(2)PFN_PHYS宏根据给定的物理页面的页帧号pfn获取其该物理页面的物理起始地址。
(3)__va() 宏用于将给定的物理地址转换为对应的内核虚拟地址。

struct page --> 页帧号pfn --> 物理页面的物理起始地址 --> 内核虚拟起始地址

1.6 page_to_virt

page_to_virt 和 page_address 功能一样:

(1)3.10.0内核版本:

#define __va(x)			((void *)((unsigned long)(x)+PAGE_OFFSET))// linux-3.10/include/asm-generic/page.h#define pfn_to_virt(pfn)	__va((pfn) << PAGE_SHIFT)#define page_to_virt(page)	pfn_to_virt(page_to_pfn(page))

(2)5.4.18内核版本:

static __always_inline void *lowmem_page_address(const struct page *page)
{return page_to_virt(page);
}#if !defined(HASHED_PAGE_VIRTUAL) && !defined(WANT_PAGE_VIRTUAL)
#define page_address(page) lowmem_page_address(page)

对于 x86_64和之前一样:

#define __va(x)			((void *)((unsigned long)(x)+PAGE_OFFSET))#define pfn_to_virt(pfn)	__va((pfn) << PAGE_SHIFT)#define page_to_virt(page)	pfn_to_virt(page_to_pfn(page))

对于aarch64:

# cat /boot/config-5.4.18-74-generic | grep CONFIG_SPARSEMEM_VMEMMAP
CONFIG_SPARSEMEM_VMEMMAP=y
#if defined(CONFIG_SPARSEMEM_VMEMMAP)#define page_to_virt(x)	({						\__typeof__(x) __page = x;					\u64 __idx = ((u64)__page - VMEMMAP_START) / sizeof(struct page);\u64 __addr = PAGE_OFFSET + (__idx * PAGE_SIZE);			\(void *)__tag_set((const void *)__addr, page_kasan_tag(__page));\
})

二、使用demo

# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/mm_types.h>
# include <linux/mm.h>
# include <linux/gfp.h>//内核模块初始化函数
static int __init lkm_init(void)
{struct page *page = alloc_pages(GFP_KERNEL, 0);unsigned long virt_address = (unsigned long)page_address(page);printk("virtual addr = 0x%lx\n", virt_address);unsigned int pfn = page_to_pfn(page);printk("pfn = %d\n", pfn);unsigned long phys_address = PFN_PHYS(pfn);printk("phys addr = 0x%lx\n", phys_address);unsigned long virt_address1 = (unsigned long)__va(phys_address);printk("virtual addr1 = 0x%lx\n", virt_address1);free_pages(virt_address, 0);return 0;}//内核模块退出函数
static void __exit lkm_exit(void)
{printk("Goodbye\n");
}module_init(lkm_init);
module_exit(lkm_exit);MODULE_LICENSE("GPL");
[159158.806456] virtual addr = 0xffff9d991dc92000
[159158.806463] pfn = 384146
[159158.806467] phys addr = 0x5dc92000
[159158.806471] virtual addr1 = 0xffff9d991dc92000

可以看到 virtual addr 和 virtual addr1 值一样。

相关文章:

Linux 内核内存管理 page_address 函数

文章目录 一、page_address1.1 page_address1.2 page_to_pfn1.3 PFN_PHYS1.4 __va(x)1.5 总结1.6 page_to_virt 二、使用demo 一、page_address 1.1 page_address 内核用 struct page 结构体来表示系统中的每个物理页面&#xff0c;该结构体用来跟踪和管理这些物理页面的使用…...

macOS使用ffmpeg与QT进行音视频推拉流

1.先启动流服务器 ./mediamtx 2.开始推流: ffmpeg -re -stream_loop -1 -i /Users/hackerx/Desktop/test.mp4 -c copy -rtsp_transport tcp -f rtsp rtsp://127.0.0.1:8554/stream 3. 安装ffmpeg 4.4 brew install ffmpeg4 4.添加ffmpeg头文件目录与库目录 5.链接ffmpeg相关库…...

ARTS打卡:双指针的尝试

替换空格 <?php class Solution {/*** param String $s* return String*/function replaceSpace($s) {$arrstr_split($s); //转化成数组foreach($arr as &$item){if($item ){//执行替换操作$item%20;}}return implode(,$arr); //数组转化成字符串返回} } 反转链表…...

JavaWeb-DAO设计模式

目录 DAO设计模式 1.认识DAO 2.DAO各部分的详解 3.DAO设计模式流程 DAO设计模式 1.认识DAO DAO(Data Acess Object 数据库访问对象)的主要功能是操作数据库&#xff0c;所以DAO在标准开发架构中数据数据层&#xff0c;以下是标准开发的架构 客户层&#xff1a;目前使用B/…...

重温git和GitHub

1.初始化本地库:让git获取到这个目录的管理权 git init 查看文件夹的文件命令&#xff1a;ll 查看文件夹的隐藏的文件命令:ll -a 查看状态的命令&#xff1a;git status cat文件名&#xff1a;查看文件内容 工作区&#xff1a;当git status时&#xff0c;名字为红色则在工作区&…...

C# WPF 中 外部图标引入iconfont,无法正常显示问题 【小白记录】

wpf iconfont 外部图标引入&#xff0c;无法正常显示问题。 1. 检查资源路径和引入格式是否正确2. 检查资源是否包含在程序集中 1. 检查资源路径和引入格式是否正确 正确的格式&#xff0c;注意字体文件 “xxxx.ttf” 应写为 “#xxxx” <TextBlock Text"&#xe7ae;…...

Hi-TRS:骨架点视频序列的层级式建模及层级式自监督学习

论文题目&#xff1a;Hierarchically Self-Supervised Transformer for Human Skeleton Representation Learning 论文下载地址&#xff1a;https://www.ecva.net/papers/eccv_2022/papers_ECCV/papers/136860181.pdf 代码地址&#xff1a;https://github.com/yuxiaochen1103…...

FPGA 之 xilinx DDS IP相位控制字及频率控制字浅析

浅析相位环在Xilinx DDS中的理解 本文仅为个人理解之用; 相关仿真结果如下:...

[鹏城杯 2022]简单包含

直接用php&#xff1a;// 有wtf 加脏数据绕过...

Required request parameter ‘XXX‘ for method parameter type XXX is not present问题

今日工作中遇到很奇葩的问题&#xff0c;用翻译软件翻译结果为 方法参数类型XXX所需的请求参数XXX不存在 也就是说前端没有给后端传值 后端的接收方式为 public Result demo(RequestParam("id") String id){}...

centOS 快速安装和配置 NVIDIA docker Container Toolkit

要在 CentOS 上正确安装和配置 NVIDIA Container Toolkit&#xff0c;您可以按照以下步骤进行操作&#xff0c;如果1和2都已经完成&#xff0c;可以直接进行第3步NVIDIA Container Toolkit安装配置。 1. 安装 NVIDIA GPU 驱动程序&#xff1a; 您可以从 NVIDIA 官方网站下载适…...

编程练习(2)

一.选择题 第一题&#xff1a; 考察转义字符和strlen函数求解字符串长度 进一步在VS中可以智能看出哪些字符是转义字符&#xff1a; 因此本体答案选择B 第二题&#xff1a; 本体较为简单&#xff0c;宏定义了三个数N,M,NUM,N值为2,M值为3&#xff0c;因此NUM值为8&#xff0c;…...

利用Figlet工具创建酷炫Linux Centos8服务器-登录欢迎界面-SHELL自动化编译安装代码

因为我们需要生成需要的特定字符,所以需要在当前服务器中安装Figlet,默认没有安装包的,其实如果我们也只要在一台环境中安装,然后需要什么字符只要复制到需要的服务器中,并不需要所有都安装。同样的,我们也可以利用此生成的字符用到脚本运行的开始起头部分,用ECHO分行标…...

Git Cherry-pick使用

概述 无论项目大小&#xff0c;当你和一群程序员一起工作时&#xff0c;处理多个 Git 分支之间的变更都会变得很困难。有时&#xff0c;与其把整个 Git 分支合并到另一个分支&#xff0c;不如选择并移动几个特定的提交。这个过程被称为 "挑拣", 即 Cherry-pick。 本…...

红帽8.5 ansible 安装和部署 |(简单版)

什么是ansible Ansible是一款基于OpenSSH开源的自动化运维工具&#xff0c;可以用它来配置系统、部署软件和编排更高级的 IT 任务&#xff0c;并且使用具有极高的安全性&#xff0c;ansible是当前市面上主流的自动化运维工具之一 为什么使用ansible 比较直观的说&#xff0c;…...

Visual Studio 2019 c++ 自定义注释 ----doxygen

可加入C 也可自定义。 <?xml version"1.0" encoding"utf-8"?> <CodeSnippets xmlns"http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"><CodeSnippet Format"1.0.0"><Header><Title>注释…...

面试题. 零矩阵

编写一种算法&#xff0c;若M N矩阵中某个元素为0&#xff0c;则将其所在的行与列清零。 示例 1&#xff1a; 输入&#xff1a; [[1,1,1],[1,0,1],[1,1,1] ] 输出&#xff1a; [[1,0,1],[0,0,0],[1,0,1] ] 示例 2&#xff1a; 输入&#xff1a; [[0,1,2,0],[3,4,5,2],[1,3…...

易语言下载器

静态网站整站下载器 https://bbs.125.la/forum.php?modviewthread&tid14791313&highlight%E4%B8%8B%E8%BD%BD%E5%99%A8 易语言 之音乐下载器 https://blog.51cto.com/u_15309652/3153642 &#xff08;File Download Assistant&#xff09;下载链接&#xff1a;https…...

原生js获取今天、昨天、近7天的时间(年月日时分秒)

有的时候我们需要将今天,昨天,近7天的时间(年月日时分秒)作为参数传递给后端,如下图: 那怎么生成这些时间呢?如下代码里,在methods里的toDay方法、yesterDay方法、weekDay方法分别用于生成今天、昨天和近7天的时间: <template><div class="box"&…...

最强自动化测试框架Playwright(29)-文件选择对象

FileChooser对象通过page.on("filechoose")事件监听。 如下代码实现点击百度搜图按钮&#xff0c;上传文件进行搜索。 from playwright.sync_api import Playwright, sync_playwright, expectdef run(playwright: Playwright) -> None:browser playwright.chro…...

【烂尾】K8S部署

0x01 初见K8S 在地下城的迷宫深处&#xff0c;有一个神奇的存在&#xff0c;它就是Kubernetes&#xff01;宛如一个勇敢的冒险者&#xff0c;它穿越着这个复杂的迷宫&#xff0c;带领着容器们战胜各种惊险的挑战。 Kubernetes就像是一位无所畏惧的剑士&#xff0c;手握着强大…...

电机故障诊断(python程序,模型为MSCNN结合LSTM结合注意力机制模型,有注释)

代码运行环境要求&#xff1a;TensorFlow版本>2.4.0&#xff0c;python版本>3.6.0 1.电机常见的故障类型有以下几种&#xff1a; 轴承故障&#xff1a;轴承是电机运转时最容易受损的部件之一。常见故障包括磨损、疲劳、过热和润滑不良&#xff0c;这些问题可能导致噪音增…...

二叉树(ACM版)

【数据结构1-2】二叉树 - 题单 - 洛谷 【数据结构】day2-树_J娇娇_的博客-CSDN博客 上学时的作业 P1827 [USACO3.4] 美国血统 American Heritage 二叉树特点写法&#xff08;非二叉树&#xff09; 截取字符串写法 #include<string> #include<cstring> #include…...

Scratch 之 如何制作鼠标框(2)—— 鼠标框框定角色

hello&#xff0c;大家好&#xff0c;欢迎来到鼠标框系列的第二课时&#xff01; 咱们废话不多说&#xff0c;直接开始 首先&#xff0c;温故知新一下&#xff0c;上个教程我们讨论了如何绘制鼠标框&#xff0c;网址&#xff1a;绘制鼠标框 你说&#xff0c;一个鼠标框&…...

爬虫逆向实战(九)--猿人学第十三题

一、数据接口分析 主页地址&#xff1a;猿人学第十三题 1、抓包 通过抓包可以发现数据接口是api/match/13 2、判断是否有加密参数 请求参数是否加密&#xff1f; 无请求头是否加密&#xff1f; 无响应是否加密&#xff1f; 无cookie是否加密&#xff1f; 在“cookie”模块…...

NeuralNLP-NeuralClassifier的使用记录(一),训练预测自己的【英文文本多分类】

NeuralNLP-NeuralClassifier的使用记录&#xff0c;训练预测自己的英文文本多分类 NeuralNLP-NeuralClassifier是腾讯开发的一个多层多分类应用工具&#xff0c;支持的任务包括&#xff0c;文本分类中的二分类、多分类、多标签&#xff0c;以及层次多标签分类。支持的文本编码…...

Pycharm社区版连接WSL2中的Mysql8.*

当前时间2023.08.13&#xff0c;Windows11中默认的WSL版本已经是2了&#xff0c;在WSL2中默认的Ubuntu版本已经是22.04&#xff0c;而Ubuntu22.04中默认的Mysql版本已经是8.*。 Wsl 2 中安装mysql WSL2中安装Mysql的方法参考自微软官方文档【开始使用适用于 Linux 的 Windows …...

前端传递参数时,form-data 和 json 的区别

在传递参数时&#xff0c;form-data 和 JSON 是两种常见的数据格式。 form-data 是一种多部分表单数据格式&#xff0c;通常用于上传文件或包含二进制数据的表单提交。它使用 multipart/form-data 格式来编码数据。在使用 form-data 格式时&#xff0c;数据会被分割成多个部分&…...

FairyGUI-Unity侧菜单扩展

目录 缘由&#xff1a; 分析&#xff1a; 准备&#xff1a; 完整代码&#xff1a; 缘由&#xff1a; 在使用FairyGUI作为项目UI开发时&#xff0c;有时会使用FairyGUI提供的Scripting Define Symbols。当前FairyGUI中的Scripting Define Symbols有&#xff1a; 骨骼动画 …...

学习笔记十八:污点、容忍度

污点、容忍度 污点、容忍度管理节点污点把k8snode2当成是生产环境专用的&#xff0c;其他node是测试的给k8snode1也打上污点 污点、容忍度 给了节点选则的主动权&#xff0c;我们给节点打一个污点&#xff0c;不容忍的pod就运行不上来&#xff0c;污点就是定义在节点上的键值属…...