You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux内核6.1.x加载内核模块时遍历虚拟内存的方法

内核6.1+遍历进程VMAs计算虚拟地址空间总大小的解决方法

内核6.1版本对vm_area_struct结构体做了重构,移除了旧的vm_next/vm_prev指针,改用struct list_head vm_node链表节点管理VMA的链接关系,直接导致你原来的遍历代码编译失败。下面给出两种可行的遍历方案,以及修改后的完整代码。

核心修改点

原来的for (vma = mm->mmap_base; vma; vma = vma->vm_next)遍历方式已失效,需要替换为基于链表的遍历逻辑,优先选择内核封装的宏或标准链表迭代器。

方案一:使用内核推荐的mm_for_each_vma宏(优先选择)

内核在<linux/mm.h>中提供了mm_for_each_vma宏,专门用于遍历进程的所有VMA,宏会自动处理链表遍历细节,兼容性和可维护性更好。

替换后的遍历代码:

mm_for_each_vma(vma, mm) {
    virt_size += vma->vm_end - vma->vm_start;
}

方案二:直接遍历mm->mmap链表

如果需要手动操作链表,可以使用list_for_each_entry迭代器,遍历挂在mm->mmap上的所有vm_area_struct节点:

list_for_each_entry(vma, &mm->mmap, vm_node) {
    virt_size += vma->vm_end - vma->vm_start;
}

完整修改后的代码

同时要添加mm_struct和task_struct的释放逻辑,避免内存泄漏:

static int __init init(void)
{
    struct task_struct *task;
    struct mm_struct *mm;
    struct vm_area_struct *vma;
    unsigned long virt_size = 0;

    // 此处省略获取目标进程task_struct的代码,例如通过find_get_task_by_vpid(target_pid)获取
    ...

    mm = get_task_mm(task);
    if (!mm) {
        printk(KERN_ERR "Failed to get mm_struct for PID %d\n", target_pid);
        put_task_struct(task);
        return -EINVAL;
    }

    // 使用mm_for_each_vma宏遍历所有VMA并计算总大小
    mm_for_each_vma(vma, mm) {
        virt_size += vma->vm_end - vma->vm_start;
    }

    printk(KERN_INFO "PID: %d, Allocated Virtual Address Space: %lu KB\n", target_pid, virt_size >> 10);

    // 释放引用的mm_struct和task_struct,避免资源泄漏
    mmput(mm);
    put_task_struct(task);

    return 0;
}

关键注意事项

  1. 引用计数管理:get_task_mm会增加mm_struct的引用计数,必须调用mmput(mm)释放;如果通过find_get_task_by_vpid等接口获取了task_struct,也要用put_task_struct释放。
  2. 空指针检查:获取mm后必须检查是否为NULL,避免后续空指针访问。
  3. 兼容性:mm_for_each_vma宏是内核推荐用法,后续内核版本如果对VMA链表结构有调整,这个宏会同步适配,无需修改业务代码。

内容的提问来源于stack exchange,提问作者Soumik Dutta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 06:22:52