|
前言操作系統(tǒng)的核心任務(wù)是對(duì)系統(tǒng)資源的管理,而重中之重就是對(duì)CPU和內(nèi)存的管理。為了使進(jìn)程擺脫系統(tǒng)內(nèi)存的制約,用戶進(jìn)程運(yùn)行在虛擬內(nèi)存之上,每個(gè)用戶進(jìn)程都擁有完整的虛擬地址空間,互不干涉。而實(shí)現(xiàn)虛擬內(nèi)存的關(guān)鍵就在于建立虛擬地址與物理地址之間的映射,因?yàn)闊o(wú)論如何數(shù)據(jù)終究要存儲(chǔ)到物理內(nèi)存中才能被記錄下來(lái)。
yftoeixwr1264076326815.png (43.59 KB, 下載次數(shù): 6)
下載附件
保存到相冊(cè)
yftoeixwr1264076326815.png
2024-10-12 00:57 上傳
如上圖所示,進(jìn)程1和進(jìn)程2都擁有完整的虛擬地址空間,虛擬地址空間又分為了用戶空間和內(nèi)核空間,對(duì)于不同的進(jìn)程面對(duì)的都是同一個(gè)內(nèi)核,其內(nèi)核空間的地址對(duì)于的物理地址都是一樣的,因而進(jìn)程1和進(jìn)程2中內(nèi)核空間的VA K地址都映射到了物理內(nèi)存的PA K地址。
一、分頁(yè)機(jī)制1.分頁(yè)機(jī)制的核心思想就是解除線性地址和物理地址的一一對(duì)應(yīng)關(guān)系,使線性地址連續(xù),而物理地址不連續(xù)。使連續(xù)的線性地址可以與任意物理內(nèi)存地址相關(guān)聯(lián),即從虛擬頁(yè)面到物理頁(yè)面的映射。而這個(gè)翻譯過(guò)程由內(nèi)存管理單元(MMU)完成,MMU接收CPU發(fā)出的虛擬地址,將其翻譯為物理地址后發(fā)送給內(nèi)存。內(nèi)存管理單元按照該物理地址進(jìn)行相應(yīng)訪問(wèn)后讀出或?qū)懭胂嚓P(guān)數(shù)據(jù),如下圖所示:
isojib1esqp64076326915.png (71.28 KB, 下載次數(shù): 4)
下載附件
保存到相冊(cè)
isojib1esqp64076326915.png
2024-10-12 00:57 上傳
2.分頁(yè)機(jī)制尋址原理通過(guò)查頁(yè)表,對(duì)于每個(gè)程序,內(nèi)存管理單元MMU都為其保存一個(gè)頁(yè)表,該頁(yè)表中存放的是虛擬頁(yè)面到物理頁(yè)面的映射。每當(dāng)為一個(gè)虛擬頁(yè)面尋找到一個(gè)物理頁(yè)面之后,就在頁(yè)表里增加一條記錄來(lái)保留該映射關(guān)系。當(dāng)然,隨著虛擬頁(yè)面進(jìn)出物理內(nèi)存,頁(yè)表的內(nèi)容也會(huì)不斷更新變化。
vslfj3wth1t64076327015.png (94.21 KB, 下載次數(shù): 6)
下載附件
保存到相冊(cè)
vslfj3wth1t64076327015.png
2024-10-12 00:57 上傳
二級(jí)頁(yè)表模式:
(1)取虛擬地址高10位*4+頁(yè)目錄表(PDE)得到頁(yè)表的物理地址
(2)取虛擬地址中間10位*4+頁(yè)表(PTE)得到頁(yè)地址
(3)取虛擬地址最后12位+頁(yè)地址得到最終的物理地址頁(yè)目錄項(xiàng)、頁(yè)表項(xiàng)結(jié)構(gòu):
3vhw0gv1yy464076327115.png (27.74 KB, 下載次數(shù): 7)
下載附件
保存到相冊(cè)
3vhw0gv1yy464076327115.png
2024-10-12 00:57 上傳
m0b5icrctod64076327215.png (27.39 KB, 下載次數(shù): 4)
下載附件
保存到相冊(cè)
m0b5icrctod64076327215.png
2024-10-12 00:57 上傳
3.啟動(dòng)分頁(yè)機(jī)制,按順序做好三件事(1)準(zhǔn)備好頁(yè)目錄表及頁(yè)表
(2)將頁(yè)表地址寫入控制寄存器cr3
(3)寄存器cr0的PG位置1,開啟頁(yè)表機(jī)制
二、x86架構(gòu)下頁(yè)表分頁(yè)機(jī)制源碼分析1.頁(yè)表結(jié)構(gòu)源碼:arch/x86/include/asm/pgtable_type.h
這段代碼是 Linux 內(nèi)核中用于定義 x86 架構(gòu)下頁(yè)表相關(guān)的宏和數(shù)據(jù)結(jié)構(gòu)的頭文件,它定義了頁(yè)表項(xiàng)的各個(gè)標(biāo)志位、頁(yè)表項(xiàng)的格式以及一些輔助函數(shù)和宏。
頁(yè)表項(xiàng)標(biāo)志位定義
#define _PAGE_BIT_PRESENT /* 表示頁(yè)面是否存在 */
#define _PAGE_BIT_RW /* 表示頁(yè)面是否可寫 */
#define _PAGE_BIT_USER /* 示頁(yè)面是否用戶可訪問(wèn) */頁(yè)表項(xiàng)的組合
#define _PAGE_SHARED
#define _PAGE_KERNEL
#define _PAGE_KERNEL_EXEC頁(yè)表項(xiàng)的類型定義
typedef struct{unsigned long pgd;}pgd_t;/* 頂級(jí)頁(yè)表目錄 */
typedef struct{unsigned long pud;}pud_t;/* 高層頁(yè)表目錄 */
typedef struct{unsigned long pmd;}pmd_t;/* 中層頁(yè)表目錄 */
typedef struct{unsigned long pte;}pte_t;/* 最底層頁(yè)表?xiàng)l目 */2.對(duì)頁(yè)表的基本操作源碼:arch/x86/mm/pgtable/pgtable.c
這段代碼包含了頁(yè)表分配、釋放、設(shè)置訪問(wèn)標(biāo)志、清除訪問(wèn)標(biāo)志等功能。這些操作是內(nèi)存管理的基本操作,通過(guò)這些操作,內(nèi)核可以高效地管理虛擬內(nèi)存和物理內(nèi)存之間的映射關(guān)系。
頁(yè)表分配和釋放
定義了頁(yè)表分配和釋放的函數(shù)。頁(yè)表分配是指為進(jìn)程或內(nèi)核分配新的頁(yè)表項(xiàng)。這通常在進(jìn)程創(chuàng)建或需要新的虛擬內(nèi)存區(qū)域時(shí)進(jìn)行。分配頁(yè)表項(xiàng)時(shí),內(nèi)核會(huì)從物理內(nèi)存中分配一塊內(nèi)存,并將其初始化為頁(yè)表項(xiàng)。
pgd_t *pgd_alloc(struct mm_struct *mm)
{
pgd_t *pgd;
pmd_t *u_pmds[MAX_PREALLOCATED_USER_PMDS];
pmd_t *pmds[MAX_PREALLOCATED_PMDS];
//PGD 分配:
pgd = _pgd_alloc();//調(diào)用 _pgd_alloc() 函數(shù)分配一個(gè)新的 PGD。如果分配失敗,跳轉(zhuǎn)到 out 標(biāo)簽.
if (pgd == NULL)
goto out;
//設(shè)置 PGD:
mm->pgd = pgd;//將分配的 PGD 賦值給當(dāng)前進(jìn)程的內(nèi)存管理結(jié)構(gòu)。
//預(yù)分配 PMD:
if (sizeof(pmds) != 0 &&
preallocate_pmds(mm, pmds, PREALLOCATED_PMDS) != 0)
goto out_free_pgd;//如果 pmds 數(shù)組的大小不為 0,調(diào)用 preallocate_pmds() 函數(shù)為當(dāng)前進(jìn)程預(yù)分配 PMD(頁(yè)中間目錄)。如果失敗,跳轉(zhuǎn)到 out_free_pgd 標(biāo)簽。
//預(yù)分配用戶 PMD:
if (sizeof(u_pmds) != 0 &&
preallocate_pmds(mm, u_pmds, PREALLOCATED_USER_PMDS) != 0)
goto out_free_pmds;
//并行虛擬化 PGD 分配:
if (paravirt_pgd_alloc(mm) != 0)
goto out_free_user_pmds;//調(diào)用 paravirt_pgd_alloc() 進(jìn)行并行虛擬化的 PGD 分配。
// 加鎖:
spin_lock(&pgd_lock);//獲取 PGD 鎖以保護(hù)后續(xù)操作。
//PGD 構(gòu)造:
pgd_ctor(mm, pgd);//調(diào)用 pgd_ctor() 初始化 PGD。
//預(yù)填充 PMD:
if (sizeof(pmds) != 0)
pgd_prepopulate_pmd(mm, pgd, pmds);//如果 pmds 不為空,調(diào)用 pgd_prepopulate_pmd() 預(yù)填充 PMD。
//預(yù)填充用戶 PMD:
if (sizeof(u_pmds) != 0)
pgd_prepopulate_user_pmd(mm, pgd, u_pmds);
//解鎖:
spin_unlock(&pgd_lock);//釋放 PGD 鎖。
//返回 PGD:
return pgd;
out_free_user_pmds:
if (sizeof(u_pmds) != 0)
free_pmds(mm, u_pmds, PREALLOCATED_USER_PMDS);
out_free_pmds:
if (sizeof(pmds) != 0)
free_pmds(mm, pmds, PREALLOCATED_PMDS);
out_free_pgd:
_pgd_free(pgd);
out:
return NULL;
}
void pgd_free(struct mm_struct *mm, pgd_t *pgd)
{
pgd_mop_up_pmds(mm, pgd);
pgd_dtor(pgd);//調(diào)用 pgd_dtor() 進(jìn)行 PGD 的析構(gòu)。
paravirt_pgd_free(mm, pgd);//調(diào)用 paravirt_pgd_free() 釋放并行虛擬化的 PGD。
_pgd_free(pgd);//調(diào)用 _pgd_free() 釋放 PGD。
}頁(yè)表級(jí)別的釋放
頁(yè)表級(jí)別的釋放函數(shù)用于釋放不同級(jí)別的頁(yè)表項(xiàng),定義了頁(yè)表級(jí)別的釋放函數(shù),如 pmd_free_tlb、pud_free_tlb 等。這些函數(shù)確保了內(nèi)存的高效利用和釋放。
#if CONFIG_PGTABLE_LEVELS > 2
void ___pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmd)
{
struct ptdesc *ptdesc = virt_to_ptdesc(pmd);//獲取 PMD 描述符,使用 virt_to_ptdesc 函數(shù)將 PMD 轉(zhuǎn)換為相應(yīng)的頁(yè)表描述符.
paravirt_release_pmd(__pa(pmd) >> PAGE_SHIFT);// 釋放 PMD,用 paravirt_release_pmd 函數(shù)釋放 PMD。__pa(pmd) 將虛擬地址轉(zhuǎn)換為物理地址,>> PAGE_SHIFT 用于獲取頁(yè)框號(hào)。
#ifdef CONFIG_X86_PAE
tlb->need_flush_all = 1;
#endif//如果啟用了 PAE,設(shè)置 tlb->need_flush_all 為 1,表示需要刷新所有 TLB 條目。
pagetable_pmd_dtor(ptdesc);//調(diào)用 pagetable_pmd_dtor 函數(shù)對(duì) PMD 進(jìn)行析構(gòu),釋放與 PMD 相關(guān)的資源。
paravirt_tlb_remove_table(tlb, ptdesc_page(ptdesc));//調(diào)用 paravirt_tlb_remove_table 函數(shù)從 TLB 中移除與 PMD 相關(guān)的頁(yè)表?xiàng)l目。
}設(shè)置和清除訪問(wèn)標(biāo)志
定義了設(shè)置和清除頁(yè)表項(xiàng)訪問(wèn)標(biāo)志的函數(shù),設(shè)置訪問(wèn)標(biāo)志是指修改頁(yè)表項(xiàng)中的標(biāo)志位,以控制頁(yè)面的訪問(wèn)權(quán)限。例如,可以設(shè)置頁(yè)面為可讀、可寫或可執(zhí)行。設(shè)置訪問(wèn)標(biāo)志時(shí),內(nèi)核會(huì)更新頁(yè)表項(xiàng)中的相應(yīng)位。設(shè)置和清除頁(yè)表項(xiàng)訪問(wèn)標(biāo)志的函數(shù)用于管理頁(yè)表項(xiàng)的訪問(wèn)權(quán)限,這些函數(shù)確保了內(nèi)存的安全性和訪問(wèn)控制。
p0a4x2a0msr64076327315.png (86.43 KB, 下載次數(shù): 5)
下載附件
保存到相冊(cè)
p0a4x2a0msr64076327315.png
2024-10-12 00:57 上傳
xioteey2zqa64076327415.png (96.27 KB, 下載次數(shù): 4)
下載附件
保存到相冊(cè)
xioteey2zqa64076327415.png
2024-10-12 00:57 上傳
int ptep_set_access_flags(struct vm_area_struct *vma,
unsigned long address, pte_t *ptep,
pte_t entry, int dirty)
{
int changed = !pte_same(*ptep, entry);//使用 pte_same 函數(shù)檢查當(dāng)前頁(yè)表項(xiàng)與新條目是否相同。如果不同,設(shè)置 changed 為 1。
if (changed && dirty)
set_pte(ptep, entry);//更新頁(yè)表項(xiàng),如果頁(yè)表項(xiàng)已更改且 dirty 為真,調(diào)用 set_pte 函數(shù)更新頁(yè)表項(xiàng)。
return changed;//返回 changed,指示頁(yè)表項(xiàng)是否已更改。
}
int ptep_test_and_clear_young(struct vm_area_struct *vma,
unsigned long addr, pte_t *ptep)
{
int ret = 0;
if (pte_young(*ptep))
ret = test_and_clear_bit(_PAGE_BIT_ACCESSED,
(unsigned long *) &ptep->pte);//檢查頁(yè)表項(xiàng)的年輕標(biāo)志,使用 pte_young 函數(shù)檢查頁(yè)表項(xiàng)是否被標(biāo)記為“年輕”。如果是,調(diào)用 test_and_clear_bit 函數(shù)清除訪問(wèn)標(biāo)志,并將結(jié)果存儲(chǔ)在 ret 中。
return ret;//返回 ret,指示是否成功清除年輕標(biāo)志。
}3.x86 架構(gòu)相關(guān)的兩級(jí)頁(yè)表操作源碼:arch/x86/include/asm/pgtable-2level.h
這段代碼主要定義了與 x86 架構(gòu)相關(guān)的兩級(jí)頁(yè)表操作,包括設(shè)置和清除 PTE、PMD 和 PUD 的函數(shù),以及交換條目和 PTE 的編碼/解碼宏和函數(shù)。通過(guò)這些定義,內(nèi)核可以正確地管理和操作兩級(jí)頁(yè)表。
頭文件保護(hù)
使用頭文件保護(hù)機(jī)制,防止重復(fù)包含。
#ifndef _ASM_X86_PGTABLE_2LEVEL_H
#define _ASM_X86_PGTABLE_2LEVEL_H錯(cuò)誤打印宏
定義了用于打印 PTE 和 PGD 錯(cuò)誤的宏。
#define pte_ERROR(e) \
pr_err("%s:%d: bad pte %08lx
", __FILE__, __LINE__, (e).pte_low)
#define pgd_ERROR(e) \
pr_err("%s:%d: bad pgd %08lx
", __FILE__, __LINE__, pgd_val(e))設(shè)置 PTE、PMD 和 PUD 的函數(shù)
static inline void native_set_pte(pte_t *ptep , pte_t pte)
{
*ptep = pte;
}//將傳入的 pte 值賦給指向的頁(yè)表項(xiàng) *ptep。
static inline void native_set_pmd(pmd_t *pmdp, pmd_t pmd)
{
*pmdp = pmd;
}//將傳入的 pmd 值賦給指向的頁(yè)中間目錄 *pmdp。
static inline void native_set_pud(pud_t *pudp, pud_t pud)
{
}//該函數(shù)的實(shí)現(xiàn)為空,表示當(dāng)前沒(méi)有對(duì) PUD 的設(shè)置操作。原子設(shè)置 PTE 的函數(shù)
static inline void native_set_pte_atomic(pte_t *ptep, pte_t pte)
{
native_set_pte(ptep, pte);//調(diào)用 native_set_pte 來(lái)設(shè)置頁(yè)表項(xiàng)。
}清除 PMD 和 PUD 的函數(shù)
static inline void native_pmd_clear(pmd_t *pmdp)
{
native_set_pmd(pmdp, __pmd(0));//調(diào)用 native_set_pmd,將 PMD 設(shè)置為 __pmd(0),將 PMD 清空或設(shè)置為無(wú)效狀態(tài)。__pmd(0) 通常表示一個(gè)無(wú)效的 PMD 條目。
}
static inline void native_pud_clear(pud_t *pudp)
{
}清除 PTE 的函數(shù)
static inline void native_pte_clear(struct mm_struct *mm,
unsigned long addr, pte_t *xp)
{
*xp = native_make_pte(0);//調(diào)用 native_make_pte(0),將頁(yè)表項(xiàng) *xp 設(shè)置為無(wú)效狀態(tài)。native_make_pte(0) 通常會(huì)返回一個(gè)表示無(wú)效頁(yè)表項(xiàng)的 PTE 值。
}獲取并清除 PTE、PMD 和 PUD 的函數(shù)
#ifdef CONFIG_SMP
static inline pte_t native_ptep_get_and_clear(pte_t *xp)
{
return __pte(xchg(&xp->pte_low, 0));
}//如果啟用了 SMP,函數(shù)使用 xchg 原子操作將 xp->pte_low 的值交換為 0,并返回原來(lái)的 PTE 值。__pte 用于將原始值轉(zhuǎn)換為 PTE 類型。
#else
#define native_ptep_get_and_clear(xp) native_local_ptep_get_and_clear(xp)
#endif//如果未啟用 SMP,使用宏定義將調(diào)用重定向到 native_local_ptep_get_and_clear
#ifdef CONFIG_SMP
static inline pmd_t native_pmdp_get_and_clear(pmd_t *xp)
{
return __pmd(xchg((pmdval_t *)xp, 0));
}//如果啟用了 SMP,函數(shù)使用 xchg 原子操作將 xp 的值交換為 0,并返回原來(lái)的 PMD 值。__pmd 用于將原始值轉(zhuǎn)換為 PMD 類型。
#else
#define native_pmdp_get_and_clear(xp) native_local_pmdp_get_and_clear(xp)
#endif//如果未啟用 SMP,使用宏定義將調(diào)用重定向到 native_local_pmdp_get_and_clear。
#ifdef CONFIG_SMP
static inline pud_t native_pudp_get_and_clear(pud_t *xp)
{
return __pud(xchg((pudval_t *)xp, 0));
}//如果啟用了 SMP,函數(shù)使用 xchg 原子操作將 xp 的值交換為 0,并返回原來(lái)的 PUD 值。__pud 用于將原始值轉(zhuǎn)換為 PUD 類型。
#else
#define native_pudp_get_and_clear(xp) native_local_pudp_get_and_clear(xp)
#endif//如果未啟用 SMP,使用宏定義將調(diào)用重定向到 native_local_pudp_get_and_clear。交換條目和 PTE 的編碼/解碼
定義了交換條目和 PTE 的編碼/解碼宏和函數(shù)。
#define SWP_TYPE_BITS 5//義交換類型所需的位數(shù),這里是 5 位。
#define _SWP_TYPE_MASK ((1U 5)//使用 BUILD_BUG_ON 宏在編譯時(shí)檢查 MAX_SWAPFILES_SHIFT 是否大于 5。如果是,則編譯失敗。這是為了確保交換文件的數(shù)量不會(huì)超過(guò)可以用位表示的最大值。
#define __swp_type(x)(((x).val >> _SWP_TYPE_SHIFT)& _SWP_TYPE_MASK)//從交換條目 x 中提取交換類型。通過(guò)右移和掩碼操作獲取類型值。
#define __swp_offset(x)((x).val >> SWP_OFFSET_SHIFT)//從交換條目 x 中提取交換偏移量。通過(guò)右移操作獲取偏移值。
#define __swp_entry(type, offset)((swp_entry_t) { \
(((type) & _SWP_TYPE_MASK) 交換 PTE 的獨(dú)占標(biāo)記
定義了用于存儲(chǔ)獨(dú)占標(biāo)記的交換 PTE 位。
#define _PAGE_SWP_EXCLUSIVE _PAGE_PSE無(wú)反轉(zhuǎn) PFN
定義了無(wú)反轉(zhuǎn) PFN 的函數(shù)。
static inline u64 protnone_mask(u64 val)
{
return 0;
}
static inline u64 flip_protnone_guard(u64 oldval, u64 val, u64 mask)
{
return val;
}
static inline bool __pte_needs_invert(u64 val)
{
return false;
}4.x86 架構(gòu)相關(guān)的三級(jí)頁(yè)表操作源碼:arch/x86/include/asm/pgtable-3level.h
這段代碼主要定義了與 x86 架構(gòu)相關(guān)的三級(jí)頁(yè)表操作,包括設(shè)置和清除 PTE、PMD 和 PUD 的函數(shù),以及交換條目和 PTE 的編碼/解碼宏和函數(shù)。通過(guò)這些定義,內(nèi)核可以正確地管理和操作三級(jí)頁(yè)表。
頭文件保護(hù)
使用頭文件保護(hù)機(jī)制,防止重復(fù)包含。
#ifndef _ASM_X86_PGTABLE_3LEVEL_H
#define _ASM_X86_PGTABLE_3LEVEL_H錯(cuò)誤打印宏
定義了用于打印 PTE、PMD 和 PGD 錯(cuò)誤的宏。
#define pte_ERROR(e)
pr_err("%s:%d: bad pte %p(%08lx%08lx)
",
__FILE__, __LINE__, &(e), (e).pte_high, (e).pte_low)//當(dāng)檢測(cè)到無(wú)效的 PTE 時(shí),調(diào)用此宏以輸出詳細(xì)的錯(cuò)誤信息。
#define pmd_ERROR(e)
pr_err("%s:%d: bad pmd %p(%016Lx)
",
__FILE__, __LINE__, &(e), pmd_val(e))//當(dāng)檢測(cè)到無(wú)效的 PMD 時(shí),調(diào)用此宏以輸出詳細(xì)的錯(cuò)誤信息。
#define pgd_ERROR(e)
pr_err("%s:%d: bad pgd %p(%016Lx)
",
__FILE__, __LINE__, &(e), pgd_val(e))//當(dāng)檢測(cè)到無(wú)效的 PGD 時(shí),調(diào)用此宏以輸出詳細(xì)的錯(cuò)誤信息。交換宏
定義了用于交換 PTE、PMD 和 PUD 的宏。
#define pxx_xchg64(_pxx, _ptr, _val) ({
_pxx##val_t *_p = (_pxx##val_t *)_ptr;//將 _ptr 轉(zhuǎn)換為指向特定類型的指針。
_pxx##val_t _o = *_p;//讀取指針 _p 指向的當(dāng)前值,并存儲(chǔ)在 _o 中。
do { } while (!try_cmpxchg64(_p, &_o, (_val)));//使用 try_cmpxchg64 函數(shù)嘗試將 _val 設(shè)置為 _p 指向的值。如果當(dāng)前值與 _o 不同,交換操作將失敗,循環(huán)將繼續(xù)嘗試,直到成功為止。
native_make_##_pxx(_o);//調(diào)用 native_make_##_pxx 函數(shù),將 _o 轉(zhuǎn)換為適當(dāng)?shù)念愋筒⒎祷亍?nbsp;
})設(shè)置 PTE、PMD 和 PUD 的函數(shù)
定義了設(shè)置 PTE、PMD 和 PUD 的函數(shù)。
static inline void native_set_pte(pte_t *ptep, pte_t pte)//native_set_pte 用于設(shè)置給定的頁(yè)表項(xiàng),pte_t *ptep: 指向要設(shè)置的頁(yè)表項(xiàng)的指針,pte_t pte: 要設(shè)置的頁(yè)表項(xiàng)值。
{
WRITE_ONCE(ptep->pte_high, pte.pte_high);//使用 WRITE_ONCE 宏將 pte_high 寫入 ptep 指向的頁(yè)表項(xiàng)的高位部分。
smp_wmb();//調(diào)用 smp_wmb(),確保在多處理器環(huán)境中寫入的順序。
WRITE_ONCE(ptep->pte_low, pte.pte_low);//使用 WRITE_ONCE 宏將 pte_low 寫入 ptep 指向的頁(yè)表項(xiàng)的低位部分。
}
static inline void native_set_pte_atomic(pte_t *ptep, pte_t pte)//以原子方式設(shè)置給定的頁(yè)表項(xiàng)
{
pxx_xchg64(pte, ptep, native_pte_val(pte));//調(diào)用 pxx_xchg64 宏,執(zhí)行原子交換操作,將 pte 的值設(shè)置到 ptep 指向的頁(yè)表項(xiàng)中。
}
static inline void native_set_pmd(pmd_t *pmdp, pmd_t pmd)//用于設(shè)置給定的頁(yè)中間目錄
{
pxx_xchg64(pmd, pmdp, native_pmd_val(pmd));//將 pmd 的值設(shè)置到 pmdp 指向的頁(yè)中間目錄中。
}
static inline void native_set_pud(pud_t *pudp, pud_t pud)//設(shè)置給定的頁(yè)上級(jí)目錄
{
#ifdef CONFIG_PAGE_TABLE_ISOLATION
pud.p4d.pgd = pti_set_user_pgtbl(&pudp->p4d.pgd, pud.p4d.pgd);//設(shè)置用戶頁(yè)表
#endif
pxx_xchg64(pud, pudp, native_pud_val(pud));//將 pud 的值設(shè)置到 pudp 指向的頁(yè)上級(jí)目錄中。
}清除 PTE、PMD 和 PUD 的函數(shù)
定義了清除 PTE、PMD 和 PUD 的函數(shù)。
static inline void native_pte_clear(struct mm_struct *mm, unsigned long addr,pte_t *ptep)//清除給定的頁(yè)表項(xiàng)(PTE),將其設(shè)置為無(wú)效狀態(tài)。
{
WRITE_ONCE(ptep->pte_low, 0);//將 pte_low 設(shè)置為 0,清除頁(yè)表項(xiàng)的低位部分。
smp_wmb();//確保在多處理器環(huán)境中寫入的順序
WRITE_ONCE(ptep->pte_high, 0);//將 pte_high 設(shè)置為 0,清除頁(yè)表項(xiàng)的高位部分。
}
static inline void native_pmd_clear(pmd_t *pmdp)//清除給定的頁(yè)中間目錄(PMD),將其設(shè)置為無(wú)效狀態(tài)。
{
WRITE_ONCE(pmdp->pmd_low, 0);//將 pmd_low 設(shè)置為 0,清除頁(yè)中間目錄的低位部分。
smp_wmb();//確保在多處理器環(huán)境中寫入的順序。
WRITE_ONCE(pmdp->pmd_high, 0);//將 pmd_high 設(shè)置為 0,清除頁(yè)中間目錄的高位部分。
}
static inline void native_pud_clear(pud_t *pudp)//清除給定的頁(yè)上級(jí)目錄(PUD)
{
}
static inline void pud_clear(pud_t *pudp)//清除給定的頁(yè)上級(jí)目錄(PUD),將其設(shè)置為無(wú)效狀態(tài)。
{
set_pud(pudp, __pud(0));//將 pud 設(shè)置為無(wú)效狀態(tài)。
}獲取并清除 PTE、PMD 和 PUD 的函數(shù)
根據(jù)是否啟用 SMP,定義了獲取并清除 PTE、PMD 和 PUD 的函數(shù)。
#ifdef CONFIG_SMP
static inline pte_t native_ptep_get_and_clear(pte_t *ptep)//獲取并清除給定的頁(yè)表項(xiàng)(PTE),pte_t *ptep: 指向要獲取并清除的頁(yè)表項(xiàng)的指針。
{
return pxx_xchg64(pte, ptep, 0ULL);將 ptep 指向的頁(yè)表項(xiàng)的值替換為 0ULL,并返回原來(lái)的 PTE 值。
}
static inline pmd_t native_pmdp_get_and_clear(pmd_t *pmdp)//獲取并清除給定的頁(yè)中間目錄(PMD)
{
return pxx_xchg64(pmd, pmdp, 0ULL);//將 pmdp 指向的頁(yè)中間目錄的值替換為 0ULL,并返回原來(lái)的 PMD 值。
}
static inline pud_t native_pudp_get_and_clear(pud_t *pudp)//獲取并清除給定的頁(yè)上級(jí)目錄(PUD)
{
return pxx_xchg64(pud, pudp);//將 pudp 指向的頁(yè)上級(jí)目錄的值替換為無(wú)效值,并返回原來(lái)的 PUD 值。
}三、arm架構(gòu)下頁(yè)表分頁(yè)機(jī)制源碼分析1.頁(yè)表分配這段代碼定義了與 ARM 架構(gòu)相關(guān)的頁(yè)表分配操作。
源碼:arc/arm/include/asm/pgtable.h
定義PGD分配和釋放函數(shù)
extern pgd_t *pgd_alloc(struct mm_struct *mm);//為給定的內(nèi)存管理結(jié)構(gòu)(mm_struct)分配一個(gè)新的頁(yè)全局目錄(PGD)。
extern void pgd_free(struct mm_struct *mm, pgd_t *pgd);//釋放之前分配的頁(yè)全局目錄(PGD)定義清理 PTE 表的函數(shù)
static inline void clean_pte_table(pte_t *pte)//清理與頁(yè)表項(xiàng)相關(guān)的緩存
{
clean_dcache_area(pte + PTE_HWTABLE_PTRS, PTE_HWTABLE_SIZE);//pte + PTE_HWTABLE_PTRS:計(jì)算要清理的緩存區(qū)域的起始地址,調(diào)用clean_dcache_area以清理指定區(qū)域的緩存。
}定義 PTE 分配函數(shù)
這段代碼展示了內(nèi)核如何為內(nèi)核空間和用戶空間分配頁(yè)表。
pte_alloc_one_kernel 函數(shù):為內(nèi)核分配一個(gè)新的頁(yè)表項(xiàng)(PTE)頁(yè),使用 __pte_alloc_one_kernel 分配PTE,如果分配成功,調(diào)用 clean_pte_table 清理新分配的頁(yè)表,返回分配的PTE指針。
pte_alloc_one 函數(shù):為用戶空間分配一個(gè)新的頁(yè)表項(xiàng)頁(yè),使用 __pte_alloc_one 分配一個(gè)頁(yè),使用 GFP_PGTABLE_USER | PGTABLE_HIGHMEM 標(biāo)志,如果分配失敗,返回 NULL,如果分配的頁(yè)不是高端內(nèi)存頁(yè),調(diào)用 clean_pte_table 清理它,返回分配的頁(yè)。
clean_pte_table 函數(shù):用于初始化新分配的頁(yè)表,可能涉及清零或設(shè)置特定的初始值。
使用CONFIG_HIGHPTE 允許在某些架構(gòu)上將頁(yè)表存儲(chǔ)在高端內(nèi)存中,這可以節(jié)省低端內(nèi)存。
GFP_PGTABLE_USER 是一個(gè)特殊的分配標(biāo)志,用于用戶空間頁(yè)表分配。
PageHighMem 檢查是否為高端內(nèi)存頁(yè)。
page_address 函數(shù):用于獲取頁(yè)的虛擬地址。
#define __HAVE_ARCH_PTE_ALLOC_ONE_KERNEL
#define __HAVE_ARCH_PTE_ALLOC_ONE
#define __HAVE_ARCH_PGD_FREE
#include
static inline pte_t *
pte_alloc_one_kernel(struct mm_struct *mm)//為內(nèi)核分配一個(gè)頁(yè)表項(xiàng)(PTE)
{
pte_t *pte = __pte_alloc_one_kernel(mm);//調(diào)用 __pte_alloc_one_kernel(mm) 分配一個(gè)頁(yè)表項(xiàng)。
if (pte)
clean_pte_table(pte);//如果分配成功,調(diào)用 clean_pte_table(pte) 清理頁(yè)表項(xiàng)相關(guān)的緩存。
return pte;//返回分配的頁(yè)表項(xiàng)指針。
}
#ifdef CONFIG_HIGHPTE
#define PGTABLE_HIGHMEM __GFP_HIGHMEM
#else
#define PGTABLE_HIGHMEM 0
#endif
static inline pgtable_t
pte_alloc_one(struct mm_struct *mm)//為用戶空間分配一個(gè)頁(yè)表項(xiàng)(PTE),struct mm_struct *mm是指向當(dāng)前進(jìn)程的內(nèi)存管理結(jié)構(gòu)的指針。
{
struct page *pte;
pte = __pte_alloc_one(mm, GFP_PGTABLE_USER | PGTABLE_HIGHMEM);//分配一個(gè)頁(yè)表項(xiàng),使用用戶空間分配標(biāo)志和高內(nèi)存標(biāo)志。
if (!pte)
return NULL;//如果分配失敗,返回 NULL。
if (!PageHighMem(pte))
clean_pte_table(page_address(pte));//如果分配的頁(yè)表項(xiàng)不在高內(nèi)存中,調(diào)用 clean_pte_table(page_address(pte)) 清理頁(yè)表項(xiàng)相關(guān)的緩存。
return pte;//返回分配的頁(yè)表項(xiàng)指針。
}定義 PMD 填充函數(shù)
__pmd_populate 函數(shù):內(nèi)部輔助函數(shù),用于填充PMD條目。
pmd_populate_kernel 函數(shù):用于內(nèi)核空間的PMD填充。
pmd_populate 函數(shù):用于用戶空間的PMD填充。
這段代碼展示了ARM架構(gòu)下內(nèi)核如何管理多級(jí)頁(yè)表結(jié)構(gòu),主要定義了與 ARM 架構(gòu)相關(guān)的頁(yè)表分配操作,包括頁(yè)表的分配和釋放、頁(yè)表的填充等。通過(guò)這些定義,內(nèi)核可以正確地管理和操作 ARM 架構(gòu)的頁(yè)表。它是內(nèi)存管理子系統(tǒng)中關(guān)鍵的一部分,負(fù)責(zé)維護(hù)虛擬內(nèi)存到物理內(nèi)存的映射關(guān)系。
static inline void __pmd_populate(pmd_t *pmdp, phys_addr_t pte,pmdval_t prot)//填充給定的頁(yè)中間目錄(PMD),將其設(shè)置為指向特定的頁(yè)表項(xiàng),pmd_t *pmdp指向要填充的 PMD 的指針,phys_addr_t pte是頁(yè)表項(xiàng)的物理地址,pmdval_t prot是PMD 的保護(hù)標(biāo)志。
{
pmdval_t pmdval = (pte + PTE_HWTABLE_OFF) | prot;//計(jì)算 pmdval,將頁(yè)表項(xiàng)地址加上 PTE_HWTABLE_OFF(頁(yè)表項(xiàng)的偏移量),并與保護(hù)標(biāo)志進(jìn)行按位或操作。
pmdp[0] = __pmd(pmdval);//將計(jì)算得到的 pmdval 轉(zhuǎn)換為 PMD 類型并賦值給 pmdp[0]。
#ifndef CONFIG_ARM_LPAE
pmdp[1] = __pmd(pmdval + 256 * sizeof(pte_t));//如果未啟用 ARM LPAE(大頁(yè)表擴(kuò)展),則填充 pmdp[1],將 pmdval 加上 256 個(gè)頁(yè)表項(xiàng)的大小。
#endif
flush_pmd_entry(pmdp);//刷新 PMD 條目,以確保更新生效。
}
static inline void pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmdp, pte_t *ptep)//在內(nèi)核空間填充 PMD。
{
__pmd_populate(pmdp, __pa(ptep), _PAGE_KERNEL_TABLE);//調(diào)用 __pmd_populate,將頁(yè)表項(xiàng)的物理地址和內(nèi)核頁(yè)表的保護(hù)標(biāo)志 _PAGE_KERNEL_TABLE 傳遞給它。
}
static inline void
pmd_populate(struct mm_struct *mm, pmd_t *pmdp, pgtable_t ptep)//填充用戶空間的 PMD。
{
extern pmdval_t user_pmd_table;//存儲(chǔ)用戶 PMD 表的保護(hù)標(biāo)志。
pmdval_t prot;
if (__LINUX_ARM_ARCH__ >= 6 && !IS_ENABLED(CONFIG_ARM_LPAE))
prot = user_pmd_table;
else
prot = _PAGE_USER_TABLE;
__pmd_populate(pmdp, page_to_phys(ptep), prot);//調(diào)用 __pmd_populate,將頁(yè)表的物理地址(通過(guò) page_to_phys(ptep) 獲。┖捅Wo(hù)標(biāo)志傳遞給它。
}2.arm架構(gòu)相關(guān)的兩級(jí)頁(yè)表操作源碼:arc/arm/include/asm/pgtable-2level.h
定義兩級(jí)頁(yè)表結(jié)構(gòu)
定義了兩級(jí)頁(yè)表結(jié)構(gòu)的相關(guān)常量和宏。
#define __PAGETABLE_PMD_FOLDED 1
#define PTRS_PER_PTE 512//每個(gè)頁(yè)表項(xiàng)(PTE)指向的條目數(shù)量,通常為 512。
#define PTRS_PER_PMD 1//每個(gè)頁(yè)中間目錄(PMD)指向的條目數(shù)量,通常為 1。
#define PTRS_PER_PGD 2048//每個(gè)頁(yè)全局目錄(PGD)指向的條目數(shù)量,通常為 2048。
#define PTE_HWTABLE_PTRS (PTRS_PER_PTE)//硬件表中每個(gè)頁(yè)表項(xiàng)的指針數(shù)量。
#define PTE_HWTABLE_OFF (PTE_HWTABLE_PTRS * sizeof(pte_t))//頁(yè)表項(xiàng)的偏移量,計(jì)算為頁(yè)表項(xiàng)數(shù)量乘以每個(gè)頁(yè)表項(xiàng)的大小。
#define PTE_HWTABLE_SIZE (PTRS_PER_PTE * sizeof(u32))//頁(yè)表項(xiàng)的總大小,計(jì)算為頁(yè)表項(xiàng)數(shù)量乘以每個(gè)頁(yè)表項(xiàng)的大小。
#define MAX_POSSIBLE_PHYSMEM_BITS 32//定義最大可能的物理內(nèi)存位數(shù),通常為 32 位,表示支持的最大物理內(nèi)存為 4GB。
#define PMD_SHIFT 21//定義PMD的位移量,通常為 21 位
#define PGDIR_SHIFT 21定義PGD 的位移量,通常為 21 位
#define PMD_SIZE (1UL 定義 Linux PTE
定義了 Linux PTE 的相關(guān)常量和宏。
#define L_PTE_VALID (_AT(pteval_t, 1) 定義 PUD 操作函數(shù)
這段代碼定義了一系列與頁(yè)表管理相關(guān)的內(nèi)聯(lián)函數(shù)和宏,特別是針對(duì)PUD(Page Upper Directory)和PMD(Page Middle Directory)級(jí)別的操作。這些定義通常用于ARM架構(gòu),特別是在不使用三級(jí)或四級(jí)頁(yè)表的情況下。
PUD(Page Upper Directory)相關(guān)函數(shù):
lhdm5run5i264076327515.png (96.1 KB, 下載次數(shù): 7)
下載附件
保存到相冊(cè)
lhdm5run5i264076327515.png
2024-10-12 00:57 上傳
PMD(Page Middle Directory)相關(guān)函數(shù):
dip3nwqmgw364076327615.png (97.2 KB, 下載次數(shù): 7)
下載附件
保存到相冊(cè)
dip3nwqmgw364076327615.png
2024-10-12 00:57 上傳
這段代碼針對(duì)的是一個(gè)簡(jiǎn)化的頁(yè)表結(jié)構(gòu),可能是兩級(jí)頁(yè)表系統(tǒng)(PGD -> PMD -> PTE),PUD級(jí)別在這個(gè)架構(gòu)中被忽略或不存在,所有PUD相關(guān)操作都是空操作或返回固定值,PMD操作被定義為直接操作頁(yè)表?xiàng)l目,包括設(shè)置、清除、復(fù)制等,使用位操作來(lái)判斷PMD的狀態(tài)(如large、leaf、bad等),這是一種常見(jiàn)的優(yōu)化技術(shù),包含了一些架構(gòu)特定的操作,如flush_pmd_entry和clean_pmd_entry,用于確保頁(yè)表更改對(duì)硬件可見(jiàn)。
#ifndef __ASSEMBLY__
static inline int pud_none(pud_t pud)
{
return 0;//檢查給定的 PUD 是否為空。當(dāng)前實(shí)現(xiàn)總是返回 0,表示 PUD 不為空。
}
static inline int pud_bad(pud_t pud)
{
return 0;//檢查給定的 PUD 是否無(wú)效。當(dāng)前實(shí)現(xiàn)總是返回 0,表示 PUD 是有效的。
}
static inline int pud_present(pud_t pud)
{
return 1;//檢查給定的 PUD 是否存在。當(dāng)前實(shí)現(xiàn)總是返回 1,表示 PUD 存在。
}
static inline void pud_clear(pud_t *pudp)
{
}//清除給定的 PUD。當(dāng)前實(shí)現(xiàn)為空,表示沒(méi)有具體的清除邏輯。
static inline void set_pud(pud_t *pudp, pud_t pud)
{
}//設(shè)置給定的 PUD。當(dāng)前實(shí)現(xiàn)為空,表示沒(méi)有具體的設(shè)置邏輯。
static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr)
{
return (pmd_t *)pud;//返回指向 PMD 的指針。
}
#define pmd_offset pmd_offset
#define pmd_pfn(pmd)(__phys_to_pfn(pmd_val(pmd) & PHYS_MASK))//從 PMD 中提取物理頁(yè)框號(hào),使用 pmd_val 獲取 PMD 的值,并與物理掩碼進(jìn)行按位與操作。
#define pmd_large(pmd)(pmd_val(pmd) & 2)//檢查 PMD 是否為大頁(yè)
#define pmd_leaf(pmd)(pmd_val(pmd) & 2)//檢查 PMD 是否為葉節(jié)點(diǎn)。
#define pmd_bad(pmd)(pmd_val(pmd) & 2)//檢查 PMD 是否無(wú)效。
#define pmd_present(pmd)(pmd_val(pmd))//檢查 PMD 是否存在。
#define copy_pmd(pmdpd,pmdps) //復(fù)制 PMD 條目并刷新目標(biāo) PMD 條目。
do {
pmdpd[0] = pmdps[0];
pmdpd[1] = pmdps[1];
flush_pmd_entry(pmdpd);
} while (0)
#define pmd_clear(pmdp)//清除 PMD 條目,將其設(shè)置為無(wú)效,并調(diào)用 clean_pmd_entry 清理相關(guān)緩存。
do {
pmdp[0] = __pmd(0);
pmdp[1] = __pmd(0);
clean_pmd_entry(pmdp);
} while (0)
#define pmd_addr_end(addr,end) (end)//返回 PMD 地址范圍的結(jié)束地址
#define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,pte,ext)//設(shè)置頁(yè)表項(xiàng)(PTE)并擴(kuò)展其屬性
#define pmd_hugewillfault(pmd)(0)
#define pmd_thp_or_huge(pmd)(0)//檢查 PMD 是否會(huì)導(dǎo)致故障或是否為大頁(yè)。當(dāng)前實(shí)現(xiàn)總是返回 0,表示不會(huì)導(dǎo)致故障或不是大頁(yè)。
#endif /* __ASSEMBLY__ */3.arm架構(gòu)相關(guān)的三級(jí)頁(yè)表操作源碼:arc/arm/include/asm/pgtable-3level.h
定義三級(jí)頁(yè)表結(jié)構(gòu)
定義了三級(jí)頁(yè)表結(jié)構(gòu)的相關(guān)常量和宏。
#define PTRS_PER_PTE 512//每個(gè)頁(yè)表項(xiàng)(PTE)指向的條目數(shù)量,通常為 512。
#define PTRS_PER_PMD 512//每個(gè)頁(yè)中間目錄(PMD)指向的條目數(shù)量,通常為 512。
#define PTRS_PER_PGD 4//每個(gè)頁(yè)全局目錄(PGD)指向的條目數(shù)量,通常為 4。
#define PTE_HWTABLE_PTRS (0)//每個(gè)頁(yè)表項(xiàng)的指針數(shù)量,這里設(shè)置為 0,表示沒(méi)有硬件表。
#define PTE_HWTABLE_OFF (0)//頁(yè)表項(xiàng)的偏移量,這里設(shè)置為 0。
#define PTE_HWTABLE_SIZE (PTRS_PER_PTE * sizeof(u64))//頁(yè)表項(xiàng)的總大小,計(jì)算為頁(yè)表項(xiàng)數(shù)量乘以每個(gè)頁(yè)表項(xiàng)的大小
#define MAX_POSSIBLE_PHYSMEM_BITS 40//定義最大可能的物理內(nèi)存位數(shù),通常為 40 位,表示支持的最大物理內(nèi)存為 1TB。
#define PGDIR_SHIFT 30//定義 PGD 的位移量,為 30 位。
#define PMD_SHIFT 21//定義 PMD 的位移量,為 21 位。
#define PMD_SIZE (1UL 定義大頁(yè)相關(guān)常量
#define HPAGE_SHIFT PMD_SHIFT//將大頁(yè)的位移量定義為 PMD 的位移量。
#define HPAGE_SIZE (_AC(1, UL) 定義 Linux PTE
#define L_PTE_VALID (_AT(pteval_t, 1) 定義 PUD 操作函數(shù)
這段代碼主要定義了與 ARM 架構(gòu)相關(guān)的三級(jí)頁(yè)表操作,包括頁(yè)表的結(jié)構(gòu)、頁(yè)表項(xiàng)的定義、PUD 和 PMD 操作函數(shù)等。通過(guò)這些定義,內(nèi)核可以正確地管理和操作 ARM 架構(gòu)的三級(jí)頁(yè)表。
ngfjsubq0jx64076327716.png (55.15 KB, 下載次數(shù): 4)
下載附件
保存到相冊(cè)
ngfjsubq0jx64076327716.png
2024-10-12 00:57 上傳
lshim5w5dmv64076327816.png (102.21 KB, 下載次數(shù): 4)
下載附件
保存到相冊(cè)
lshim5w5dmv64076327816.png
2024-10-12 00:57 上傳
df0co35lq5h64076327916.png (55.42 KB, 下載次數(shù): 6)
下載附件
保存到相冊(cè)
df0co35lq5h64076327916.png
2024-10-12 00:57 上傳
#ifndef __ASSEMBLY__
#define pud_none(pud) (!pud_val(pud))//檢查 PUD 是否為空
#define pud_bad(pud) (!(pud_val(pud) & 2))//檢查 PUD 是否無(wú)效
#define pud_present(pud) (pud_val(pud))//檢查 PUD 是否存在
#define pmd_table(pmd) ((pmd_val(pmd) & PMD_TYPE_MASK) == PMD_TYPE_TABLE)//檢查 PMD 是否指向頁(yè)表
#define pmd_sect(pmd) ((pmd_val(pmd) & PMD_TYPE_MASK) == PMD_TYPE_SECT)//檢查 PMD 是否指向段
#define pmd_large(pmd) pmd_sect(pmd)//檢查 PMD 是否為大頁(yè)
#define pmd_leaf(pmd) pmd_sect(pmd)//檢查 PMD 是否為葉節(jié)點(diǎn)
#define pud_clear(pudp) //清除 PUD,將其設(shè)置為無(wú)效,并清理相關(guān)緩存
do {
*pudp = __pud(0);
clean_pmd_entry(pudp);
} while (0)
#define set_pud(pudp, pud) //設(shè)置 PUD,并刷新相關(guān)條目
do {
*pudp = pud;
flush_pmd_entry(pudp);
} while (0)
static inline pmd_t *pud_pgtable(pud_t pud)
{
return __va(pud_val(pud) & PHYS_MASK & (s32)PAGE_MASK);
}
#define pmd_bad(pmd) (!(pmd_val(pmd) & 2))//檢查 PMD 是否無(wú)效
#define copy_pmd(pmdpd,pmdps) //復(fù)制 PMD 條目并刷新目標(biāo) PMD
do {
*pmdpd = *pmdps;
flush_pmd_entry(pmdpd);
} while (0)
#define pmd_clear(pmdp) //清除 PMD,將其設(shè)置為無(wú)效,并清理相關(guān)緩存
do {
*pmdp = __pmd(0);
clean_pmd_entry(pmdp);
} while (0)
#define __HAVE_ARCH_PTE_SAME
#define pte_same(pte_a,pte_b) ((pte_present(pte_a) ? pte_val(pte_a) & ~PTE_EXT_NG \
: pte_val(pte_a)) \
== (pte_present(pte_b) ? pte_val(pte_b) & ~PTE_EXT_NG \
: pte_val(pte_b)))
#define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,__pte(pte_val(pte)|(ext)))
#define pte_huge(pte) (pte_val(pte) && !(pte_val(pte) & PTE_TABLE_BIT))
#define pte_mkhuge(pte) (__pte(pte_val(pte) & ~PTE_TABLE_BIT))
#define pmd_isset(pmd, val) ((u32)(val) == (val) ? pmd_val(pmd) & (val) \
: !!(pmd_val(pmd) & (val)))
#define pmd_isclear(pmd, val) (!(pmd_val(pmd) & (val)))
#define pmd_present(pmd) (pmd_isset((pmd), L_PMD_SECT_VALID))//檢查 PMD 是否存在
#define pmd_young(pmd) (pmd_isset((pmd), PMD_SECT_AF))//檢查 PMD 是否為年輕狀態(tài)
#define pte_special(pte) (pte_isset((pte), L_PTE_SPECIAL))
static inline pte_t pte_mkspecial(pte_t pte)
{
pte_val(pte) |= L_PTE_SPECIAL;
return pte;
}
#define pmd_write(pmd) (pmd_isclear((pmd), L_PMD_SECT_RDONLY))//檢查 PMD 是否可寫
#define pmd_dirty(pmd) (pmd_isset((pmd), L_PMD_SECT_DIRTY))//檢查 PMD 是否已被修改
#define pmd_hugewillfault(pmd) (!pmd_young(pmd) || !pmd_write(pmd))
#define pmd_thp_or_huge(pmd) (pmd_huge(pmd) || pmd_trans_huge(pmd))
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
#define pmd_trans_huge(pmd) (pmd_val(pmd) && !pmd_table(pmd))
#endif
#define PMD_BIT_FUNC(fn,op) \
static inline pmd_t pmd_##fn(pmd_t pmd) { pmd_val(pmd) op; return pmd; }
PMD_BIT_FUNC(wrprotect, |= L_PMD_SECT_RDONLY);
PMD_BIT_FUNC(mkold, &= ~PMD_SECT_AF);
PMD_BIT_FUNC(mkwrite_novma, &= ~L_PMD_SECT_RDONLY);
PMD_BIT_FUNC(mkdirty, |= L_PMD_SECT_DIRTY);
PMD_BIT_FUNC(mkclean, &= ~L_PMD_SECT_DIRTY);
PMD_BIT_FUNC(mkyoung, |= PMD_SECT_AF);
#define pmd_mkhuge(pmd) (__pmd(pmd_val(pmd) & ~PMD_TABLE_BIT))
#define pmd_pfn(pmd) (((pmd_val(pmd) & PMD_MASK) & PHYS_MASK) >> PAGE_SHIFT)//從 PMD 中提取物理頁(yè)框號(hào)PFN
#define pfn_pmd(pfn,prot) (__pmd(((phys_addr_t)(pfn) = TASK_SIZE);
if (pmd_val(pmd) & L_PMD_SECT_NONE)
pmd_val(pmd) &= ~L_PMD_SECT_VALID;
if (pmd_write(pmd) && pmd_dirty(pmd))
pmd_val(pmd) &= ~PMD_SECT_AP2;
else
pmd_val(pmd) |= PMD_SECT_AP2;
*pmdp = __pmd(pmd_val(pmd) | PMD_SECT_nG);
flush_pmd_entry(pmdp);
}
#endif /* __ASSEMBLY__ */四、示例實(shí)踐(1)代碼功能
編寫一個(gè)內(nèi)核模塊示例來(lái)演示如何在內(nèi)核中創(chuàng)建一個(gè)頁(yè)表映射。
(2)實(shí)現(xiàn)步驟
a.編寫內(nèi)核模塊代碼。
b.編譯內(nèi)核模塊。
c.加載內(nèi)核模塊。
d.檢查內(nèi)核日志以分析結(jié)果。
e.卸載內(nèi)核模塊。
(3)代碼實(shí)現(xiàn)
pagetable_example.c
#include
#include
#include
#include
#include
#include
static int __init pagetable_example_init(void) {
struct page *page;
void *vaddr;
unsigned long paddr;
// 分配一個(gè)物理頁(yè)
page = alloc_page(GFP_KERNEL);
if (!page) {
pr_err("Failed to allocate page
");
return -ENOMEM;
}
// 獲取物理地址
paddr = page_to_phys(page);
pr_info("Allocated page at physical address: 0x%lx
", paddr);
// 映射到內(nèi)核虛擬地址空間
vaddr = vmap(&page, 1, VM_MAP, PAGE_KERNEL);
if (!vaddr) {
pr_err("Failed to map page to virtual address
");
__free_page(page);
return -ENOMEM;
}
pr_info("Mapped page to virtual address: %p
", vaddr);
// 寫入數(shù)據(jù)到映射的內(nèi)存
strcpy(vaddr, "Hello, kernel page!");
// 讀取數(shù)據(jù)
pr_info("Data in mapped page: %s
", (char *)vaddr);
// 取消映射
vunmap(vaddr);
// 釋放物理頁(yè)
__free_page(page);
return 0;
}
static void __exit pagetable_example_exit(void) {
pr_info("Page table example module unloaded
");
}
module_init(pagetable_example_init);
module_exit(pagetable_example_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple example of page table mapping in the Linux kernel");編譯和加載內(nèi)核模塊
obj-m += pagetable_example.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean加載內(nèi)核模塊
sudo insmod pagetable_example.ko檢查內(nèi)核日志以分析結(jié)果
paup4ajpv0m64076328016.png (21.41 KB, 下載次數(shù): 4)
下載附件
保存到相冊(cè)
paup4ajpv0m64076328016.png
2024-10-12 00:57 上傳
卸載內(nèi)核模塊
sudo rmmod pagetable_example分析結(jié)果
物理地址:0x199fe000 是分配的物理頁(yè)的地址。
虛擬地址:000000007e626547 是映射到內(nèi)核虛擬地址空間的地址。
數(shù)據(jù):Hello, kernel page! 是寫入和讀取的測(cè)試數(shù)據(jù)?偨Y(jié)
這個(gè)例子展示了如何在內(nèi)核中分配一個(gè)物理頁(yè),將其映射到內(nèi)核虛擬地址空間,進(jìn)行讀寫操作,然后取消映射并釋放物理頁(yè)。
五、心得體會(huì)通過(guò)對(duì)這一部分的學(xué)習(xí),我真切的感受到了線性地址和物理地址的映射關(guān)系、內(nèi)存空間的分配,以及在內(nèi)存中讀寫信息的過(guò)程,對(duì)內(nèi)核也有了進(jìn)步一的認(rèn)識(shí),Linux內(nèi)核頁(yè)表映射確實(shí)是一個(gè)深入理解操作系統(tǒng)內(nèi)存管理機(jī)制的重要過(guò)程。此次分析的內(nèi)核源碼為L(zhǎng)inux6.8.0。
猜你喜歡:
WiFi6+藍(lán)牙+星閃,三合一開發(fā)板,真香!
Github上熱門 C 語(yǔ)言項(xiàng)目匯總!
嵌入式,可測(cè)試性軟件設(shè)計(jì)!
一些低功耗軟件設(shè)計(jì)的要點(diǎn)!
嵌入式 C 保護(hù)結(jié)構(gòu)體的方式
實(shí)用 | 10分鐘教你通過(guò)網(wǎng)頁(yè)點(diǎn)燈
談?wù)勄度胧杰浖募嫒菪裕?/strong>
分享一個(gè)嵌入式代碼生成器設(shè)計(jì)思路!
點(diǎn)擊閱讀原文,查看更多分享。 |
|