插件窝 干货文章 一文读懂Linux内核内存映射与页表

一文读懂Linux内核内存映射与页表

内存 映射 PTE 地址 248    来源:    2025-05-13

Linux内核内存映射与页表机制详解

一、内存管理基础概念

1. 虚拟内存与物理内存

  • 虚拟内存:进程看到的连续地址空间,由操作系统管理
  • 物理内存:实际的RAM硬件资源
  • 映射关系:通过页表建立虚拟地址到物理地址的转换

2. 内存分页机制

  • 页(Page):Linux通常使用4KB大小的内存块
  • 页帧(Page Frame):物理内存中的4KB单元
  • 页表(Page Table):存储虚拟页到物理页帧的映射关系

二、Linux多级页表结构

1. 四级页表结构(x86_64架构)

PGD (Page Global Directory)
  |
PUD (Page Upper Directory)
  |
PMD (Page Middle Directory)
  |
PTE (Page Table Entry)

2. 地址转换过程

虚拟地址分解(以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. 加上页内偏移得到最终物理地址

三、内核内存映射机制

1. 直接映射区域(线性映射)

  • 范围:通常为物理内存的直接映射(如x86_64上的0xffff880000000000~)
  • 特点:虚拟地址与物理地址有固定偏移,转换简单快速

2. 动态内存映射

  • vmalloc区域:用于分配不连续的物理内存
  • kmap区域:临时映射高端内存
  • ioremap:映射设备内存到内核空间

3. 进程地址空间

  • 用户空间:0x0000000000000000 ~ 0x00007fffffffffff
  • 内核空间:0xffff800000000000 ~ 0xffffffffffffffff

四、页表相关操作

1. 页表遍历API

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);

2. 页表项操作

// 设置页表项
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);        // 是否被访问

3. TLB管理

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); // 刷新单页

五、内存映射实现

1. 创建内存映射

int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
                    unsigned long pfn, unsigned long size, pgprot_t prot);

2. 缺页异常处理

缺页处理流程: 1. 检查访问地址是否有效 2. 检查访问权限 3. 分配物理页帧 4. 建立页表映射 5. 重新执行触发异常的指令

六、性能优化技术

1. 大页(Huge Page)支持

  • 2MB/1GB大页:减少TLB miss,提高性能
  • 使用方式:

    # 挂载hugetlbfs
    mount -t hugetlbfs hugetlbfs /dev/hugepages
    
    # 预留大页
    echo 20 > /proc/sys/vm/nr_hugepages
    

2. 透明大页(THP)

  • 自动将普通页合并为大页
  • 控制参数:

    # 查看THP状态
    cat /sys/kernel/mm/transparent_hugepage/enabled
    
    # 配置THP
    echo "madvise" > /sys/kernel/mm/transparent_hugepage/enabled
    

七、调试与监控

1. 查看进程内存映射

cat /proc/<pid>/maps
pmap -x <pid>

2. 页表信息查看

# 需要内核配置CONFIG_PTDUMP
cat /sys/kernel/debug/page_tables

3. 性能监控工具

perf stat -e dtlb-load-misses,dtlb-store-misses

八、常见问题与解决方案

1. 内存泄漏检测

  • 使用kmemleak检测内核内存泄漏 bash echo scan > /sys/kernel/debug/kmemleak cat /sys/kernel/debug/kmemleak

2. 内存碎片问题

  • 解决方案:
    • 使用内存规整(compaction)
    • 适当配置vm.min_free_kbytes
    • 考虑使用CMA(连续内存分配器)

3. 页表占用内存过多

  • 优化方案:
    • 使用大页减少页表项数量
    • 调整进程地址空间布局
    • 限制过度使用mmap的场景

通过深入理解Linux内核的内存映射与页表机制,开发人员可以更好地进行系统调优、性能分析和问题诊断。