PGD (Page Global Directory)
|
PUD (Page Upper Directory)
|
PMD (Page Middle Directory)
|
PTE (Page Table Entry)
虚拟地址分解(以4KB页为例):
63 48 47 39 38 30 29 21 20 12 11 0
+-------------+----------------+----------------+----------------+----------------+-------------+
| 符号扩展位 | PGD索引(9位) | PUD索引(9位) | PMD索引(9位) | PTE索引(9位) | 页内偏移(12位) |
+-------------+----------------+----------------+----------------+----------------+-------------+
转换过程: 1. 从CR3寄存器获取PGD基地址 2. 用PGD索引找到PUD表项 3. 用PUD索引找到PMD表项 4. 用PMD索引找到PTE表项 5. 用PTE索引找到物理页帧 6. 加上页内偏移得到最终物理地址
pgd_t *pgd_offset(struct mm_struct *mm, unsigned long address);
pud_t *pud_offset(pgd_t *pgd, unsigned long address);
pmd_t *pmd_offset(pud_t *pud, unsigned long address);
pte_t *pte_offset_kernel(pmd_t *pmd, unsigned long address);
// 设置页表项
void set_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pte);
// 检查页表项标志
int pte_present(pte_t pte); // 页是否在内存中
int pte_write(pte_t pte); // 是否可写
int pte_dirty(pte_t pte); // 是否被修改
int pte_young(pte_t pte); // 是否被访问
void flush_tlb_all(void); // 刷新所有TLB
void flush_tlb_mm(struct mm_struct *mm); // 刷新指定地址空间的TLB
void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr); // 刷新单页
int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
unsigned long pfn, unsigned long size, pgprot_t prot);
缺页处理流程: 1. 检查访问地址是否有效 2. 检查访问权限 3. 分配物理页帧 4. 建立页表映射 5. 重新执行触发异常的指令
使用方式:
# 挂载hugetlbfs
mount -t hugetlbfs hugetlbfs /dev/hugepages
# 预留大页
echo 20 > /proc/sys/vm/nr_hugepages
控制参数:
# 查看THP状态
cat /sys/kernel/mm/transparent_hugepage/enabled
# 配置THP
echo "madvise" > /sys/kernel/mm/transparent_hugepage/enabled
cat /proc/<pid>/maps
pmap -x <pid>
# 需要内核配置CONFIG_PTDUMP
cat /sys/kernel/debug/page_tables
perf stat -e dtlb-load-misses,dtlb-store-misses
bash
echo scan > /sys/kernel/debug/kmemleak
cat /sys/kernel/debug/kmemleak
通过深入理解Linux内核的内存映射与页表机制,开发人员可以更好地进行系统调优、性能分析和问题诊断。