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 值一樣。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/39166.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/39166.shtml
英文地址,請注明出處:http://en.pswp.cn/news/39166.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MySQL面試題一

MySQL 索引使用有哪些注意事項呢&#xff1f; 可以從兩個維度回答這個問題&#xff1a; 索引哪些情況會失效&#xff0c;索引不適合哪些場景 索引哪些情況會失效 查詢條件包含or&#xff0c;會導致索引失效。隱式類型轉換&#xff0c;會導致索引失效&#xff0c; 例如age字…

Idea的基本使用帶案例---詳細易懂

一.idea是什么 有專業人士說&#xff0c;idea是天生適合做微軟&#xff0c;當時我還想肯定是夸大其詞了&#xff0c;但當你用起來的時候確實很爽&#xff0c;&#x1f60a;&#x1f60a; ntelliJ IDEA是一種集成開發環境&#xff08;IDE&#xff09;&#xff0c;由JetBrains開發…

后仿知識總結

基本詞語的概念&#xff1a; &#xff08;1&#xff09;Place&Routing pr&#xff0c;布局布線 sdf基礎概念&#xff1a; 靜態時序分析圣經翻譯計劃——附錄B&#xff1a;SDF&#xff08;上&#xff09; - 知乎 (zhihu.com) 靜態時序分析圣經翻譯計劃——附錄B&#x…

繼承和多態C++

這里寫目錄標題 繼承public、protected、private 修飾類的成員public、protected、private 指定繼承方式改變訪問權限 C繼承時的名字遮蔽問題基類成員函數和派生類成員函數不構成重載C基類和派生類的構造函數構造函數的調用順序基類構造函數調用規則 C基類和派生類的析構函數C多…

MTK Android隱藏NavigationBar

安卓MTK屏蔽NavigationBar, 在SDK中通過搜索關鍵字修改&#xff0c;可適用大部分MTK及安卓版本&#xff0e; 方法介紹 搜索device/mediatek與device/mediateksample下的.xml把config_showNavigationBar值置為false 如下為搜索指令 find device/mediatek -name “*.xml” | xa…

系統架構師---開發方法---敏捷開發

目錄 前言 極限編程 四大價值觀 溝通 簡單 反饋 勇氣 尊重&#xff1a; 十二個最佳實踐 計劃游戲 小型發布 隱喻 簡單設計 測試先行 重構 結對編程 集體代碼所所有制 持續集成 每周工作40小時 現場客戶 編碼標準 前言 2001年2月&#xff0c;在美國的猶他州…

Grafana展示k8s中pod的jvm監控面板/actuator/prometheus

場景 為保障java服務正常運行&#xff0c;對服務的jvm進行監控&#xff0c;通過使用actuator組件監控jvm情況&#xff0c;使用prometheus對數據進行采集&#xff0c;并在Grafana展現。 基于k8s場景 prometheus數據收集 配置service的lable&#xff0c;便于prometheus使用labl…

LVS負載均衡集群

目錄 集群 什么是集群 (含義) 集群的分類 LVS 負載均衡器的集群架構 負載均衡器的群集工作模式 LVS負載均衡器的調度算法 LVS組成作用 組成 作用 LVS群集創建與管理 創建步驟 ipvsadm工具 LVS-NAT部署實戰 1、部署共享存儲 2、配置節點服務器&#xff08;后端服…

JetPack Compose 學習筆記(持續整理中...)

1.為什么要學&#xff1f; 1.命令式和聲明式 UI大戰,個人認為命令式UI自定義程度較高,能更深入到性能,內存優化方面,而申明式UI 是現在主流的設計,比如React,React Native,Flutter,Swift UI等等,現在性能也逐漸在變得更好 2.還有一個原因compose 是KMM 是完整跨平臺的UI基礎 3.…

kafka使用心得(一)

kafka入門 一種分布式的、基于發布/訂閱的消息系統&#xff0c;scala編寫&#xff0c;具備快速、可擴展、可持久化的特點。 基本概念 topic 主題 partition 分區&#xff0c;一個topic下可以有多個partition&#xff0c;消息是分散到多個partition里存儲的&#xff0c;part…

劍指Offer48.最長不含重復字符的子字符串 C++

1、題目描述 請從字符串中找出一個最長的不包含重復字符的子字符串&#xff0c;計算該最長子字符串的長度。 示例 1: 輸入: “abcabcbb” 輸出: 3 解釋: 因為無重復字符的最長子串是 “abc”&#xff0c;所以其長度為 3。 示例 2: 輸入: “bbbbb” 輸出: 1 解釋: 因為無重復字…

圖像處理技巧形態學濾波之膨脹操作

1. 引言 歡迎回來&#xff0c;我的圖像處理愛好者們&#xff01;今天&#xff0c;讓我們繼續研究圖像處理領域中的形態學計算。在本篇中&#xff0c;我們將重點介紹腐蝕操作的反向效果膨脹操作。 閑話少說&#xff0c;我們直接開始吧&#xff01; 2. 膨脹操作原理 膨脹操作…

macOS CLion 使用 bits/stdc++.h

macOS 下 CLion 使用 bits/stdc.h 頭文件 terminal運行 brew install gccCLion里配置 -D CMAKE_CXX_COMPILER/usr/local/bin/g-11

Visual Studio 2022 中解決使用scanf報錯的方法(一勞永逸)

目錄 【前言】 一、scanf報錯示例 二、解決使用scanf報錯的方法 解決方法1&#xff08;不推薦&#xff09; 解決方法2&#xff08;不推薦&#xff09; 解決方法3&#xff08;強烈推薦&#xff09; 第一步 第二步 第三步 三、效果演示&#xff08;方法三&#xff09; …

根據一棵樹的兩種遍歷構造二叉樹

題目 給定兩個整數數組 preorder 和 inorder &#xff0c;其中 preorder 是二叉樹的先序遍歷&#xff0c; inorder 是同一棵樹的中序遍歷&#xff0c;請構造二叉樹并返回其根節點。 示例 1: 輸入: preorder [3,9,20,15,7], inorder [9,3,15,20,7] 輸出: [3,9,20,null,null,…

Unity-Linux部署WebGL項目MIME類型添加

在以往的文章中有提到過使用IIS部署WebGL添加MIME類型使WebGL項目在瀏覽器中能夠正常加載&#xff0c;那么如果咱們做的是商業項目&#xff0c;往往是需要部署在學校或者云服務器上面的&#xff0c;大部分情況下如果項目有接口或者后臺管理系統&#xff0c;后臺基本都會使用Lin…

機器學習筆記:李宏毅ChatGPT Finetune VS Prompt

1 兩種大語言模型&#xff1a;GPT VS BERT 2 對于大語言模型的兩種不同期待 2.1 “專才” 2.1.1 成為專才的好處 Is ChatGPT A Good Translator? A Preliminary Study 2023 Arxiv 箭頭方向指的是從哪個方向往哪個方向翻譯 表格里面的數值越大表示翻譯的越好 可以發現專門做翻…

Ceph入門到精通-Linux下Ceph源碼編譯和GDB調試

Ceph版本&#xff1a;14.2.22 Linux版本&#xff1a;ubuntu-server 18.04 第一部分 下載Ceph源碼 1.1 配置Ceph源碼鏡像源 Ceph源碼是托管在Github上&#xff0c;由于某些原因&#xff0c;國內訪問Github網站很慢&#xff0c;所以需要從其他途徑加速獲取源碼。Github官方給出…

【ubuntu18.04】01-network-manager-all.yaml和interfaces和resolv.conf各有什么區別和聯系

文章目錄 01-network-manager-all.yaml、interfaces 和 resolv.conf 是與網絡配置相關的文件&#xff0c;它們在網絡設置中有著不同的作用和使用方式。 01-network-manager-all.yaml: 這是一個配置文件&#xff0c;通常在 Ubuntu 系統上使用 NetworkManager 進行網絡管理時使用…

ChatGPT?保密嗎?它有哪些潛在風險?如何規避?

自2022年11月公開發布以來&#xff0c;ChatGPT已成為許多企業和個人的必備工具&#xff0c;但隨著該技術越來越多地融入我們的日常生活&#xff0c;人們很自然地想知道&#xff1a;ChatGPT是否是保密的。 問&#xff1a;ChatGPT保密嗎&#xff1f; 答&#xff1a;否&#xff0…