Linux6.8最新版本asm路径下分页管理源码详解

asm-generic文件目录为/usr/src/linux-headers-6.8.0-45-generic/include/asm-generic,本次分析了其中三个头文件pgtable-nop4d.hpgtable-nopud.hpgtable-nopmd.h

pgtable-nop4d.h 分析

该文件是为不支持四级页表(P4D)的架构设计的,它通过“折叠”P4D层级到PGD(Page Global Directory)层级来简化页表结构。以下是对代码的分析:

/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _PGTABLE_NOP4D_H
#define _PGTABLE_NOP4D_H

#ifndef __ASSEMBLY__

/* 定义了一个标志,表明P4D层级被折叠到了PGD层级 */
#define __PAGETABLE_P4D_FOLDED 1

/* 定义了一个p4d_t类型,其只包含一个pgd_t类型的成员,使得P4D的所有功能映射到PGD上。 */
typedef struct { pgd_t pgd; } p4d_t;

/* 这些宏定义了P4D层级应该具有的属性,但由于P4D被折叠到PGD,它们实际上使用了PGD的相应值 */
#define P4D_SHIFT		PGDIR_SHIFT
#define PTRS_PER_P4D		1
#define P4D_SIZE		(1UL << P4D_SHIFT)
#define P4D_MASK		(~(P4D_SIZE-1))

/*
* 这里的 “pgd_xxx() ”函数对于折叠式两级设置来说是微不足道的:
* p4d 永远不会坏,p4d 永远存在(因为它被折叠到 pgd 条目中)
*/
/* 
* 因为P4D总是存在且不会被记为坏(因为它被折叠到PGD中),
* 所以pgd_none和pgd_bad总是返回0,pgd_present总是返回1,
* 而pgd_clear则不执行任何操作
*/
static inline int pgd_none(pgd_t pgd)		{ return 0; }
static inline int pgd_bad(pgd_t pgd)		{ return 0; }
static inline int pgd_present(pgd_t pgd)	{ return 1; }
static inline void pgd_clear(pgd_t *pgd)	{ }
/* 由于折叠,pgd_ERROR函数实际检查的是PGD的错误状态 */
#define p4d_ERROR(p4d)				(pgd_ERROR((p4d).pgd))

/* 这些宏在P4D被折叠到PGD的情况下没有实际作用,因此被定义为空操作 */
#define pgd_populate(mm, pgd, p4d)		do { } while (0)
#define pgd_populate_safe(mm, pgd, p4d)		do { } while (0)
/* (p4ds 被折叠到 pgds 中,因此不会被实际调用,但通用内联函数需要该定义) */
/* 
* 这个宏使用set_p4d来设置PGD的值,由于P4D被折叠到PGD,
* 这里使用了p4d_t类型的强制转换来适配函数签名。
*/
#define set_pgd(pgdptr, pgdval)	set_p4d((p4d_t *)(pgdptr), (p4d_t) { pgdval })

/* 该函数返回指向PGD的指针,也是P4D的指针,因为P4D和PGD在物理上相同 */
static inline p4d_t *p4d_offset(pgd_t *pgd, unsigned long address)
{
return (p4d_t *)pgd;
}

/* 下两宏用于获取或设置p4d_t结构中的PGD值 */
#define p4d_val(x)				(pgd_val((x).pgd))
#define __p4d(x)				((p4d_t) { __pgd(x) })

/*
* 这些宏被重定义以兼容P4D的概念,尽管它们实际上直接操作PGD条目。
* pgd_page返回与PGD条目相关联的页面结构,而pgd_page_vaddr返回PGD表的虚拟地址。
*/
#define pgd_page(pgd)				(p4d_page((p4d_t){ pgd }))
#define pgd_page_vaddr(pgd)			((unsigned long)(p4d_pgtable((p4d_t){ pgd })))

/* 分配和释放p4d都很简单:1-entry p4d 位于pgd内,因此没有与之相关的额外内存 */
/* 由于P4D被折叠到PGD,因此不需要为P4D分配或释放额外的内存,因此这些宏被定义为空操作 */
#define p4d_alloc_one(mm, address)		NULL
#define p4d_free(mm, x)				do { } while (0)
#define p4d_free_tlb(tlb, x, a)			do { } while (0)

/* 定义了P4D,即PGD的地址范围的结束点,由于P4D与PGD相同,因此直接返回给定的结束地址 */
#undef  p4d_addr_end
#define p4d_addr_end(addr, end)			(end)

#endif /* __ASSEMBLY__ */
#endif /* _PGTABLE_NOP4D_H */

pgtable-nopud.h 分析:

​该头文件是 Linux 内核中用于处理没有物理页上级目录PUD(Page Upper Directory)的页表结构。在一些架构中,页表可能只包含页全局目录(PGD)、页上级目录(PUD)、页中间目录(PMD)和页表项(PTE)等中的几个层级,而不是全部。这个文件定义了在没有 PUD 层级的情况下的宏和函数。下面是对文件中各个宏和函数作用的详细分析:

/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _PGTABLE_NOPUD_H
#define _PGTABLE_NOPUD_H

#ifndef __ASSEMBLY__

#include <asm-generic/pgtable-nop4d.h>

/* 定义为 1,表示 PUD 层级被折叠了,在五级分页下直接由 P4D替代。 */
#define __PAGETABLE_PUD_FOLDED 1

/* 让pud类型由p4d组成,可以正确地确定大小,并允许访问p4d,而无需转换。*/
/* 定义pud_t结构体仅包含p4d_t类型的成员p4d,这表示在没有独立PUD层级的架构中,PUD的功能由P4D承担 */
typedef struct { p4d_t p4d; } pud_t;

/* 这些宏定义了被折叠层级PUD的属性,如页表项的偏移量PUD_SHIFT、每个PUD可指向的页中间目录的数量PTRS_PER_PUD,因为PUD被折叠为1,PUD管理的内存区域大小PUD_SIZE和地址掩码PUD_MASK */
#define PUD_SHIFT	P4D_SHIFT
#define PTRS_PER_PUD	1
#define PUD_SIZE  	(1UL << PUD_SHIFT)
#define PUD_MASK  	(~(PUD_SIZE-1))

/* 
* 这里的“p4d_xxx()”函数对于折叠的两级设置来说是微不足道的:
* pud永远不会坏并且永远存在(因为它被折叠到了p4d这条目中)
*/
/* 这些内联函数在PUD被折叠的上下文中是空的或返回固定值 */
static inline int p4d_none(p4d_t p4d)		{ return 0; }
static inline int p4d_bad(p4d_t p4d)		{ return 0; }
static inline int p4d_present(p4d_t p4d)	{ return 1; }
static inline void p4d_clear(p4d_t *p4d)	{ }
/* 通过P4D实现报告PUD相关的错误 */
#define pud_ERROR(pud)				(p4d_ERROR((pud).p4d))

/* 因为PUD被折叠,故在没有独立PUD层级的系统中是空的 */
#define p4d_populate(mm, p4d, pud)		do { } while (0)
#define p4d_populate_safe(mm, p4d, pud)		do { } while (0)
/* (puds被折叠到p4ds中,因此不会被实际调用,但通用内联函数需要该定义) */
/* 设置P4D的值,但这里通过set_pud和类型转换来间接设置,以适配PUD被折叠的情况 */
#define set_p4d(p4dptr, p4dval)	set_pud((pud_t *)(p4dptr), (pud_t) { p4dval })

/* 函数返回给定P4D指针作为PUD指针,因为PUD被折叠进P4D */
static inline pud_t *pud_offset(p4d_t *p4d, unsigned long address)
{
	return (pud_t *)p4d;
}
#define pud_offset pud_offset

/* 用于获取PUD的值和构造PUD结构体 */
#define pud_val(x)				(p4d_val((x).p4d))
#define __pud(x)				((pud_t) { __p4d(x) })

/* 通过内在为P4D的PUD获取页框和页表页 */
#define p4d_page(p4d)				(pud_page((pud_t){ p4d }))
#define p4d_pgtable(p4d)			((pud_t *)(pud_pgtable((pud_t){ p4d })))

/* 分配和释放一个pud是微不足道的:1-entry pud 位于p4d内,因此没有额外内存 */
/* 由于PUD被折叠而不需要分配或释放内存,所以这些函数都是空的 */
#define pud_alloc_one(mm, address)		NULL
#define pud_free(mm, x)				do { } while (0)
#define pud_free_tlb(tlb, x, a)		        do { } while (0)

/* 用于计算给定地址范围内由实为P4D的PUD管理的结束地址 */
#undef  pud_addr_end
#define pud_addr_end(addr, end)			(end)

#endif /* __ASSEMBLY__ */
#endif /* _PGTABLE_NOPUD_H */

pgtable-nopmd.h 分析:

该文件是 Linux 内核中用于处理没有物理页上级目录PUD(Page Upper Directory)的页表结构头文件。在一些架构中,页表可能只包含页全局目录PGD、页上级目录PUD、页中间目录PMD和页表项PTE等中的几个层级,而不是全部。该文件定义了在没有 PUD 层级的情况下的宏和函数,下面是对源码的详细分析:

/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _PGTABLE_NOPMD_H
#define _PGTABLE_NOPMD_H

#ifndef __ASSEMBLY__

#include <asm-generic/pgtable-nopud.h>

struct mm_struct;

/* 定义为1,表示PMD层级被折叠了,直接由PUD或可能的其他层级替代 */
#define __PAGETABLE_PMD_FOLDED 1

/* pmd类型由一个pud组成,这样不仅大小合适,而且我们还可以访问该pmd折叠成的pud时而无需转换 */
/* 定义包含pud_t类型成员pud的结构体pmd_t,这表示在没有独立PMD层级的架构中,PMD的功能由PUD承担 */
typedef struct { pud_t pud; } pmd_t;

/* 这些宏定义了被折叠层级PMD的一些属性,如页表项的偏移量PMD_SHIFT、因为PMD被折叠为1的每个PMD可指向的页表项的数量PTRS_PER_PMD、PMD管理的内存区域大小PMD_SIZE和地址掩码PMD_MASK */
#define PMD_SHIFT	PUD_SHIFT
#define PTRS_PER_PMD	1
#define PMD_SIZE  	(1UL << PMD_SHIFT)
#define PMD_MASK  	(~(PMD_SIZE-1))

/*
 * 这里的 “pud_xxx() ”函数对于折叠式两级设置来说是微不足道的:
 * pmd从不出错,pmd始终存在(因为它被折叠到了pud条目中)
 */
/* 这些函数在PMD被折叠的上下文中是空的或返回固定值,因为 PMD 不存在或总是有效的 */
static inline int pud_none(pud_t pud)		{ return 0; }
static inline int pud_bad(pud_t pud)		{ return 0; }
static inline int pud_present(pud_t pud)	{ return 1; }
static inline int pud_user(pud_t pud)		{ return 0; }
static inline int pud_leaf(pud_t pud)		{ return 0; }
static inline void pud_clear(pud_t *pud)	{ }
/* 通过 PUD 实现报告 PMD 相关的错误 */
#define pmd_ERROR(pmd)				(pud_ERROR((pmd).pud))

/* 宏定义为空操作,因为在没有PMD的情况下无需填充PMD */
#define pud_populate(mm, pmd, pte)		do { } while (0)

/* (pmds 被折叠到 puds 中,因此不会被实际调用,但通用内联函数需要定义。) */
/* 用于设置PUD的值,此处通过set_pmd和类型转换来间接设置,以适配PMD被折叠的情况 */
#define set_pud(pudptr, pudval)			set_pmd((pmd_t *)(pudptr), (pmd_t) { pudval })

/* 因为PMD被折叠进PUD,故函数返回给定PUD指针作为PMD指针 */
static inline pmd_t * pmd_offset(pud_t * pud, unsigned long address)
{
	return (pmd_t *)pud;
}
#define pmd_offset pmd_offset

/* 用于获取PMD的值和构造PMD结构体 */
#define pmd_val(x)				(pud_val((x).pud))
#define __pmd(x)				((pmd_t) { __pud(x) } )

/* 通过实为PUD的PMD获取页框和页表页 */
#define pud_page(pud)				(pmd_page((pmd_t){ pud }))
#define pud_pgtable(pud)			((pmd_t *)(pmd_page_vaddr((pmd_t){ pud })))

/*(分配和释放pmd非常简单:1-entry pmd位于pud内,因此没有额外的内存。) */
/* 由于PMD被折叠,这些函数都是空的或返回NULL */
#define pmd_alloc_one(mm, address)		NULL
static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd)
{
}
#define pmd_free_tlb(tlb, x, a)		do { } while (0)

/* 用于计算给定地址范围内由实为PUD的PMD管理的结束地址 */
#undef  pmd_addr_end
#define pmd_addr_end(addr, end)			(end)

#endif /* __ASSEMBLY__ */
#endif /* _PGTABLE_NOPMD_H */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值