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

阅读Linux 4.0内核RMAP机制的代码,画出父子进程之间VMA、AVC、anon_vma和page等数据结构之间的关系图。

Linux 4.0 内核 RMAP 机制深度解析

 

1. RMAP 核心数据结构关系图

关键关系说明:

 

1.每个进程的 mm_struct 管理多个 VMA 区域

 

2.每个 VMA 通过 anon_vma_chain (AVC) 链接到 anon_vma

 

3.父进程和子进程共享同一个 anon_vma 结构

 

4.匿名页面通过 page→mapping 指向所属的 anon_vma

 

5.anon_vma 通过红黑树管理所有关联的 anon_vma_chain

 

 

2. 核心数据结构详解

 

2.1 struct vm_area_struct (VMA)

 

struct vm_area_struct {

    struct mm_struct *vm_mm; // 所属内存管理器

    unsigned long vm_start; // 起始虚拟地址

    unsigned long vm_end; // 结束虚拟地址

    struct list_head anon_vma_chain; // AVC 链表头

    struct anon_vma *anon_vma; // 指向anon_vma

};

 

2.2 struct anon_vma_chain (AVC)

 

struct anon_vma_chain {

    struct vm_area_struct *vma; // 关联的VMA

    struct anon_vma *anon_vma; // 关联的anon_vma

    struct list_head same_vma; // 同VMA的AVC链表

    struct rb_node rb; // 红黑树节点

};

 

2.3 struct anon_vma

 

struct anon_vma {

    struct rw_semaphore rwsem; // 读写锁

    atomic_t refcount; // 引用计数

    struct anon_vma *root; // 根anon_vma

    struct rb_root rb_root; // AVC红黑树根

};

 

2.4 struct page

 

struct page {

    unsigned long flags; // 标志位

    struct address_space *mapping; // 指向anon_vma或address_space

    atomic_t _mapcount; // 映射计数

    atomic_t _count; // 引用计数

};

 

3. 父子进程创建时的 RMAP 建立流程

 

3.1 进程 fork 的关键代码 (kernel/fork.c)

 

static __latent_entropy struct task_struct *copy_process(...)

{

    // 创建新进程的mm_struct

    retval = -EAGAIN;

    p = dup_task_struct(current);

    

    // 复制内存映射

    retval = copy_mm(clone_flags, p);

}

 

static int copy_mm(unsigned long clone_flags, struct task_struct *tsk)

{

    // 共享或复制内存映射

    if (clone_flags & CLONE_VM) {

        atomic_inc(&current->mm->mm_users);

        tsk->mm = current->mm;

    } else {

        tsk->mm = dup_mm(tsk);

    }

}

 

static struct mm_struct *dup_mm(struct task_struct *tsk)

{

    // 复制整个mm_struct

    mm = allocate_mm();

    memcpy(mm, oldmm, sizeof(*mm));

    

    // 复制所有VMA

    dup_mmap(mm, oldmm);

}

 

3.2 VMA 复制过程 (kernel/fork.c)

 

static __latent_entropy int dup_mmap(struct mm_struct *mm,

        struct mm_struct *oldmm)

{

    // 遍历所有VMA进行复制

    for (mpnt = oldmm->mmap; mpnt; mpnt = mpnt->vm_next) {

        // 复制VMA结构

        tmp = kmem_cache_alloc(vm_area_cachep, GFP_KERNEL);

        *tmp = *mpnt;

        

        // 关键:复制anon_vma链接

        anon_vma_fork(tmp, mpnt);

    }

}

 

3.3 anon_vma 复制核心 (mm/rmap.c)

 

int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma)

{

    // 获取父进程的anon_vma

    struct anon_vma *anon_vma = pvma->anon_vma;

    

    // 如果父进程有anon_vma链接

    if (anon_vma) {

        // 创建新的anon_vma_chain

        struct anon_vma_chain *avc = anon_vma_chain_alloc();

        

        // 将新VMA连接到父进程的anon_vma

        anon_vma_chain_link(vma, avc, anon_vma);

        

        // 增加父进程anon_vma的引用计数

        anon_vma->refcount++;

    }

    

    // 将父进程的anon_vma复制给子进程

    vma->anon_vma = anon_vma;

}

 

4. 代码案例:父子进程共享页面

 

#include <stdio.h>

#include <stdlib.h>

#include <sys/types.h>

#include <unistd.h>

 

int main(void)

{

    int *shared_var = mmap(NULL, 4096, PROT_READ|PROT_WRITE, 

                          MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);

    *shared_var = 42;

    

    printf("Parent[%d] initial value: %d\n", getpid(), *shared_var);

    

    pid_t pid = fork();

    

    if (pid == 0) { // Child process

        printf("Child[%d] start value: %d\n", getpid(), *shared_var);

        *shared_var = 100; // COW will happen here

        printf("Child[%d] modified value: %d\n", getpid(), *shared_var);

    } else { // Parent process

        sleep(1); // Wait child modify

        printf("Parent[%d] final value: %d\n", getpid(), *shared_var);

    }

    

    return 0;

}

 

运行结果:

 

Parent[1234] initial value: 42

Child[1235] start value: 42

Child[1235] modified value: 100

Parent[1234] final value: 42

5. 内核 RMAP 工作流程分析

 

5.1 COW 触发过程

 

子进程写共享页面触发缺页异常

 

// arch/x86/mm/fault.c

dotraplinkage void __kprobes do_page_fault(...)

{

    __do_page_fault(regs, error_code, address);

}

 

static void __kprobes __do_page_fault(...)

{

    // 处理写保护错误

    if (error_code & PF_WRITE)

        flags |= FAULT_FLAG_WRITE;

        

    fault = handle_mm_fault(mm, vma, address, flags);

}

 

5.2 COW 处理 (mm/memory.c)

 

static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,

        unsigned long address, pte_t *page_table, pmd_t *pmd,

        spinlock_t *ptl, pte_t orig_pte)

{

    // 获取原始页面

    old_page = vm_normal_page(vma, address, orig_pte);

    

    // 检查是否可以重用(只有1个引用)

    if (PageAnon(old_page) && !PageKsm(old_page) && 

        page_count(old_page) == 1) {

        reuse = true;

    }

    

    // 需要创建新页面的情况

    if (unlikely(!reuse)) {

        // 分配新页面

        new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, address);

        

        // 复制父页内容

        copy_user_highpage(new_page, old_page, address, vma);

        

        // 设置新页的反向映射

        page_add_new_anon_rmap(new_page, vma, address);

    }

}

 

 

5.3 RMAP 反向查找 (mm/rmap.c)

 

void rmap_walk(struct page *page, struct rmap_walk_control *rwc)

{

    // 匿名页处理

    if (PageAnon(page)) {

        anon_vma = page_anon_vma(page);

        if (!anon_vma)

            return;

            

        // 遍历所有链接的VMA

        anon_vma_interval_tree_foreach(avc, &anon_vma->rb_root,

                     pgoff, pgoff) {

            struct vm_area_struct *vma = avc->vma;

            

            // 执行指定操作

            rwc->rmap_one(page, vma, address, rwc->arg);

        }

    }

}

 

6. 性能优化关键点

 

6.1 KSM 合并相同页面

 

// mm/ksm.c

static int unmerge_and_remove_all_rmap_items(void)

{

    // 合并相同内容页面

    err = try_to_merge_with_ksm_page(rmap_item, page, NULL);

}

 

6.2 反向映射高效查找

 

// mm/interval_tree.c

void anon_vma_interval_tree_insert(

    struct anon_vma_chain *node, struct rb_root *root)

{

    // 红黑树插入操作

    rb_insert_augmented(&node->rb, root, &anon_vma_interval_tree_augment);

}

 

6.3 RCU 保护锁机制

 

// mm/rmap.c

struct anon_vma *page_lock_anon_vma_read(struct page *page)

{

    // 使用RCU保护并发访问

    rcu_read_lock();

    anon_vma = (struct anon_vma *) (anon_mapping - PAGE_MAPPING_ANON);

    if (atomic_inc_return(&anon_vma->refcount) > 1)

        gotref = true;

}

 

7. 实际生产环境配置建议

 

7.1 /proc/sys/vm 优化参数

 

 

# 增大相同页面扫描范围

echo 1000 > /proc/sys/vm/ksm_pages_to_scan

 

# 减少扫描间隔

echo 100 > /proc/sys/vm/ksm_sleep_millisecs

 

# 开启透明大页合并

echo always > /sys/kernel/mm/transparent_hugepage/khugepaged/defrag

 

7.2 cgroup 内存控制

 

# 限制进程组写时复制内存

echo 2G > /sys/fs/cgroup/memory/user.slice/memory.max

 

# 设置KSM共享策略

echo 1 > /sys/fs/cgroup/memory/user.slice/memory.ksm_share

 

总结

 

Linux 内核的 RMAP 机制通过精巧的数据结构设计实现高效的反向映射:

 

父子进程共享:通过 anon_vma 连接父进程和子进程的 VMA

写时复制:COW 机制延迟物理内存分配直到真正需要时

高效查找:红黑树结构实现 O(log n) 复杂度的反向映射

内存优化:KSM 机制合并相同页面提高内存利用率

锁机制优化:RCU 读写锁保护并发访问

这种设计在高内存压力场景下(如虚拟机、容器环境)尤其重要,能显著减少内存冗余,提高系统整体性能。

 

http://www.xdnf.cn/news/1429525.html

相关文章:

  • innovus: postRoute如何加shielding
  • ARM - GPIO 标准库开发
  • 【Python3教程】Python3高级篇之XML解析
  • 3dmax烘培插件3dmax法线贴图烘焙教程glb和gltf元宇宙灯光效果图烘焙烘焙光影贴图支持VR渲染器
  • 10 51单片机之DS1302实时时钟
  • Java集合源码解析之ArrayList
  • 网络共享协议
  • 【Vue2 ✨】 Vue2 入门之旅(五):组件化开发
  • 车载刷写架构 --- ECU软件更新怎么保证数据的正确性?
  • MATLAB矩阵及其运算(三)矩阵的创建
  • 应用层:HTTP/HTTPS协议
  • 【Python数据可视化:Matplotlib高级技巧】
  • 高效数据传输的秘密武器:Protobuf
  • 京东商品详情商品详情接口技术实现:从数据抓取到结构化解析全方案
  • LeetCode 777.在LR字符串中交换相邻字符
  • C++ 面试高频考点 力扣 852. 山脉数组的峰顶索引 二分查找 题解 每日一题
  • 【Linux笔记】命令行与vim基础
  • 单元测试总结2
  • MTK Linux DRM分析(二十六)- MTK mtk_drm_ddp_xxx.c
  • Spring Boot 3.5.3 集成 Log4j2 日志系统
  • 从spring MVC角度理解HTTP协议及Request-Response模式
  • 异常处理小妙招——1.别把“数据库黑话”抛给用户:论异常封装的重要性
  • 图像 OSD层数据 显示--OSD LOGO单色黑色显示,按区域大小申请MMZ内存的优缺点分析
  • 2022版Unity创建时没有2D灯光(2D Light),没有Global LIght2D怎么办?
  • Java集合遍历的方法有哪些
  • 使用Spark计算WordCount
  • 美团 LongCat 开源大模型60 亿参数 MoE 架构,赋能开发者加速 AI 应用落地
  • vue2中如何使用Ant Design Vue 中的 Tooltip 文字提示
  • 242. 有效的字母异位词| 349. 两个数组的交集
  • 通信中FDD和TDD的区别