当前位置: 首页 > news >正文

在Linux 2.4.x内核中,如何从一个page找到所有映射该页面的VMA?反向映射可以带来哪些便利?

 

Linux 2.4.x内核中的反向映射详解

 

在Linux 2.4.x内核中,反向映射机制尚未引入(反向映射在2.6内核才正式加入)。2.4.x内核中要查找映射特定页面的VMA是相对低效的,需要通过遍历进程和页表实现。

 

一、在2.4.x内核中如何从page找到VMA

 

在Linux 2.4.x中,没有直接的方法可以从一个struct page找到所有映射它的VMA。需要遍历所有进程的内存描述符和页表:

 

// Linux 2.4.x内核中查找映射特定页面的VMA

void find_vmas_for_page(struct page *page, struct mm_struct **mm_list, struct vm_area_struct **vma_list)

{

    struct task_struct *p;

    unsigned long pfn = page_to_pfn(page);

    

    // 遍历所有进程

    for_each_process(p) {

        struct mm_struct *mm = p->mm;

        struct vm_area_struct *vma;

        

        if (!mm)

            continue;

            

        // 锁定内存描述符

        spin_lock(&mm->page_table_lock);

        

        // 遍历进程的所有VMA

        for (vma = mm->mmap; vma; vma = vma->vm_next) {

            unsigned long addr;

            pte_t *pte;

            

            // 检查地址范围

            if (!(vma->vm_flags & VM_SHARED))

                continue;

                

            // 遍历VMA内的所有页表项

            for (addr = vma->vm_start; addr < vma->vm_end; addr += PAGE_SIZE) {

                pte = lookup_pte(mm, addr);

                if (!pte)

                    continue;

                    

                if (pte_present(*pte) && pfn == pte_pfn(*pte)) {

                    // 找到映射关系,添加到列表

                    add_to_list(mm_list, vma_list, mm, vma);

                }

            }

        }

        

        // 解锁内存描述符

        spin_unlock(&mm->page_table_lock);

    }

}

 

这段代码的几个关键问题:

 

性能极差:需要遍历系统中所有进程的页表

 

复杂锁操作:需要获取每个进程的page_table_lock

 

无法处理共享内存:仅能查找VM_SHARED类型的映射

 

 

二、反向映射带来的便利

 

当反向映射在2.6内核被引入后,带来了显著的优化:

 

 

场景 2.4.x方法 反向映射方法 优势

 

查找映射页面的VMA O(N^2) 遍历所有进程和页表 通过struct page->mapping直接访问 性能从O(N)提升到O(1)

 

页面回收 扫描所有进程的页表 直接访问反向映射列表 减少70%的页面回收延迟

 

共享内存管理 无法高效处理 priority search tree优化映射 提高共享内存访问效率

 

NUMA优化 无NUMA感知 知晓页面的所有访问位置 改进内存迁移和NUMA调度

 

反向映射的核心优势:

 

1.页面回收效率提升:在内存压力下,页面回收速度显著加快

 

2.页面迁移优化:透明大页迁移、NUMA负载均衡的基础

 

3.共享内存性能:高效处理共享库和共享内存映射

 

4.简化内核代码:无需复杂的遍历逻辑,减少锁竞争

 

三、反向映射代码案例(模拟2.6+内核)

 

以下是一个模拟2.6+内核反向映射机制的概念性实现:

 

#include <linux/mm.h>

#include <linux/rmap.h>

#include <linux/list.h>

 

// 定义反向映射元素结构

struct rmap_element {

    struct vm_area_struct *vma; // 映射的VMA

    unsigned long address; // 页面在VMA中的地址

    struct list_head list; // 链表元素

};

 

// 将VMA添加到页面的反向映射中

int page_add_rmap(struct page *page, struct vm_area_struct *vma,

                  unsigned long address)

{

    struct rmap_element *rme;

    

    // 为新映射元素分配内存

    rme = kmalloc(sizeof(*rme), GFP_KERNEL);

    if (!rme)

        return -ENOMEM;

    

    // 初始化映射元素

    rme->vma = vma;

    rme->address = address;

    

    // 添加到页面的反向映射列表

    spin_lock(&page->rmap_lock);

    list_add(&rme->list, &page->rmap_list);

    spin_unlock(&page->rmap_lock);

    

    return 0;

}

 

// 从页面移除VMA映射

void page_remove_rmap(struct page *page, struct vm_area_struct *vma,

                      unsigned long address)

{

    struct rmap_element *rme;

    struct list_head *pos, *next;

    

    spin_lock(&page->rmap_lock);

    

    // 遍历反向映射列表

    list_for_each_safe(pos, next, &page->rmap_list) {

        rme = list_entry(pos, struct rmap_element, list);

        

        // 匹配VMA和地址

        if (rme->vma == vma && rme->address == address) {

            list_del(&rme->list);

            kfree(rme);

            break;

        }

    }

    

    spin_unlock(&page->rmap_lock);

}

 

// 查找所有映射页面的VMA

int page_get_rmap(struct page *page, struct vm_area_struct **vmas,

                  int max_vmas)

{

    struct rmap_element *rme;

    int count = 0;

    

    spin_lock(&page->rmap_lock);

    

    // 遍历反向映射列表

    list_for_each_entry(rme, &page->rmap_list, list) {

        if (count >= max_vmas)

            break;

            

        vmas[count++] = rme->vma;

    }

    

    spin_unlock(&page->rmap_lock);

    

    return count;

}

 

// 使用反向映射进行页面迁移

int migrate_page(struct page *page, struct page *new_page)

{

    struct rmap_element *rme;

    int ret = 0;

    

    // 复制反向映射信息

    INIT_LIST_HEAD(&new_page->rmap_list);

    spin_lock(&page->rmap_lock);

    list_splice_init(&page->rmap_list, &new_page->rmap_list);

    spin_unlock(&page->rmap_lock);

    

    // 更新所有映射的页表项

    list_for_each_entry(rme, &new_page->rmap_list, list) {

        pte_t *pte;

        

        // 获取页表项锁

        spin_lock(&rme->vma->vm_mm->page_table_lock);

        

        // 查找页表项

        pte = lookup_pte(rme->vma->vm_mm, rme->address);

        if (!pte) {

            spin_unlock(&rme->vma->vm_mm->page_table_lock);

            ret = -EFAULT;

            continue;

        }

        

        // 更新页表项指向新页面

        set_pte_at(rme->vma->vm_mm, rme->address, pte,

                   mk_pte(new_page, pte_prot(*pte)));

                   

        spin_unlock(&rme->vma->vm_mm->page_table_lock);

    }

    

    return ret;

}

 

反向映射在实际操作中的流程:

 

1.映射添加:

 

进程映射页面时,调用page_add_rmap()添加反向映射项

 

添加到页面的rmap_list链表中

 

2.映射移除:

 

进程取消映射时,调用page_remove_rmap()移除项

 

从链表删除并释放元素

 

3.查找映射:

 

通过page_get_rmap()直接获取所有映射的VMA

 

O(1)时间复杂度

 

4.页面迁移:

 

迁移时复制反向映射信息到新页面

 

遍历映射更新所有进程的页表

 

无需扫描整个系统

 

四、反向映射的演进

 

Linux内核中反向映射的演进历史:

 

版本 机制 特点

2.4.x 无反向映射 通过遍历进程页表查找映射关系

2.6.0 原始反向映射 每个页面维护映射链表

2.6.11 对象化反向映射 将反向映射与地址空间关联

2.6.20 优先搜索树 高效处理共享映射

4.0+ 简化反向映射 移除不必要的数据结构

 

五、实际应用场景

 

反向映射的实际应用示例:

 

1.页面回收:

 

static int shrink_page_list(struct list_head *page_list)

{

    struct page *page;

    

    while (!list_empty(page_list)) {

        page = list_entry(page_list->prev, struct page, lru);

        

        // 1. 检查页面是否被映射

        if (page_mapped(page)) {

            struct rmap_element *rme;

            

            // 2. 解锁操作:清除所有映射

            list_for_each_entry(rme, &page->rmap_list, list) {

                ptep_clear_flush(rme->vma, rme->address, 

                                 pte_offset_map(rme->vma->vm_mm, rme->address));

            }

        }

        

        // 3. 回收页面

        free_unref_page(page);

    }

}

 

2.大页迁移:

 

int migrate_pages(struct list_head *pages)

{

    struct page *page, *next;

    

    list_for_each_entry_safe(page, next, pages, lru) {

        // 1. 分配新页面

        struct page *new_page = alloc_page(GFP_HIGHUSER);

        

        // 2. 复制内容

        copy_page(page_address(new_page), page_address(page));

        

        // 3. 使用反向映射迁移

        migrate_page(page, new_page);

        

        // 4. 释放原页面

        __free_page(page);

    }

}

 

六、总结

 

2.4.x的限制:

 

缺乏反向映射机制

查找映射关系需要遍历所有进程和页表

性能差,特别在大内存系统上

反向映射的价值:

 

极大优化了页面回收效率

为内存迁移、共享内存和NUMA优化提供基础

减少锁竞争,简化内核代码逻辑

内核演进的启示:

 

反向映射是Linux内存管理的重要创新

解决了可扩展性问题,为支持TB级内存铺平道路

展示了内核数据结构优化对系统整体性能的影响

通过理解反向映射的演变,我们可以更好地把握Linux内存管理的设计哲学,以及为现代大型系统设计高效数据结构的重要性。

http://www.dtcms.com/a/363891.html

相关文章:

  • Vue基础知识-localStorage 与 sessionStorage
  • Linux 文本处理三大利器:命令小工具和sed
  • leetcode-每日一题-3025. 人员站位的方案数 I-C语言
  • Web 开发 17
  • Mysql数据库基础(上)
  • MySQL-Undo Log(回滚日志)
  • 【Python3教程】Python3高级篇之多线程
  • 通用的二叉数迭代方法
  • 国内真实的交换机、路由器和分组情况
  • 容器日志加密传输在海外云服务器环境的配置标准与实施指南
  • Go结构体详解:核心概念与实战技巧
  • 计算机Python毕业设计推荐:基于Django的酒店评论文本情感分析系统【源码+文档+调试】
  • 移动端网页设计vm+rem,和px实现方式对比
  • ansible变量+管理机密
  • ansible循环+判断(with,loop,when,if,for)
  • 视觉语言模型VLM部署:基于tensorrt和tensorrt-llm的C++代码
  • 基于SpringBoot的广科大在线图书管理系统设计与实现(代码+数据库+LW)
  • Arduino Uno与4×4矩阵键盘联动完全指南
  • 百度智能云,除了AI还有啥?
  • 数据结构——树(04二叉树,二叉搜索树专项,代码练习)
  • 腾讯混元翻译模型Hunyuan-MT-7B开源:小参数量大能量,获得30项国际冠军
  • LoRA至今历程回顾(74)
  • 9.2C++——匿名对象、友元、常成员函数和常对象、运算符重载
  • 【72页PPT】企业供应链计划管理APS及运输管理OTM一体化解决方案(附下载方式)
  • 急招 MySQL / PG DBA,欢迎自荐或推荐朋友!推荐有奖!
  • GAN 网络的核心功能与深度解析
  • C语言:归并排序和计数排序
  • 从面试实战看Java技术栈深度:一个程序员的进阶之路
  • [邮件服务器core] doc www | 安装与构建
  • 前端开发中经常提到的iframe、DOM是什么?