以下展示了 pte_page() 的实现细节,
x86 pte
arch/x86/include/asm/pgtable-2level.h
/* No inverted PFNs on 2 level page tables */
static inline u64 protnone_mask(u64 val)
{
return 0;
}
arch/x86/include/asm/page_types.h, arch/x86/include/asm/pgtable_64_types.h, arch/x86/include/asm/pgtable_64.h
#define PAGE_SHIFT 12
#define PAGE_MASK (~(PAGE_SIZE-1)) // = 0xffff,ffff,ffff,f000
#define __PHYSICAL_MASK_SHIFT 32 // or 52 for 64位处理器
#define __PHYSICAL_MASK ((phys_addr_t)((1ULL << __PHYSICAL_MASK_SHIFT) - 1)) // = 0x0000,0000,ffff,ffff
#define PHYSICAL_PAGE_MASK (((signed long)PAGE_MASK) & __PHYSICAL_MASK) // =0xffff,f000
/* Extracts the PFN from a (pte|pmd|pud|pgd)val_t of a 4KB page */
#define PTE_PFN_MASK ((pteval_t)PHYSICAL_PAGE_MASK) // = 0xffff,f000
#define __VMEMMAP_BASE_L4 0xffffea0000000000UL
unsigned long vmemmap_base __ro_after_init = __VMEMMAP_BASE_L4;
EXPORT_SYMBOL(vmemmap_base);
# define VMEMMAP_START vmemmap_base // 在虚拟地址空间中分配一段地址来安放struct page数组
#define vmemmap ((struct page *)VMEMMAP_START) // 不同架构的vmemmap的实现不一样
static inline pteval_t native_pte_val(pte_t pte)
{
return pte.pte;
}
include/asm-generic/memory_model.h
#elif defined(CONFIG_SPARSEMEM_VMEMMAP) // 稀疏型内存模型
/* memmap is virtually contiguous. */
#define __pfn_to_page(pfn) (vmemmap + (pfn))
#define __page_to_pfn(page) (unsigned long)((page) - vmemmap)
#if defined(CONFIG_FLATMEM) // 另一种 平坦型内存模型
#define __pfn_to_page(pfn) (mem_map + ((pfn) - ARCH_PFN_OFFSET)) // ARCH_PFN_OFFSET is pfn_base
#define __page_to_pfn(page) ((unsigned long)((page) - mem_map) + \
ARCH_PFN_OFFSET)
#define pfn_to_page __pfn_to_page
每一个pf (page frame)有一个一一对应的page数据结构, 假设物理内存从x地址开始,那么第一个pfn码就是(x>>PAGE_SHIFT)
arch/x86/include/asm/pgtable.h
#define pte_val(x) native_pte_val(x)
#define pte_page(pte) pfn_to_page(pte_pfn(pte))
static inline unsigned long pte_pfn(pte_t pte)
{
phys_addr_t pfn = pte_val(pte);
pfn ^= protnone_mask(pfn); // 对于pgtable-2level, pfn ^= 0 = pfn
return (pfn & PTE_PFN_MASK) >> PAGE_SHIFT; // pfn = ( pfn & 0xffff,f000 ) >> 12
}
PTE 结构:
References