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

内核vmalloc内存映射至用户空间的实现及故障排查

用户态与内核态共享内存的问题及修复

核心需求

需在用户态与内核态间共享大内存空间,因kmalloc分配连续物理内存易出现不足,选择vmalloc分配非连续内存,但映射到用户态时遇到页错误等问题。

当前代码与错误信息

自定义mmap代码片段

{
    int ret;
    unsigned long size = vma->vm_end - vma->vm_start;
    pr_info("vma->vm_start: %p\n", vma->vm_start);
    pr_info("vma->vm_end: %p\n", vma->vm_end);
    pr_info("memory: %p\n", memory);
    unsigned long pfn = vmalloc_to_pfn(k_qp);
    pr_info("pfn: %ld\n", pfn);
    if (size > PAGE_SIZE)
    {
        pr_err("size too large\n");
        return -EINVAL;
    }
    ret = remap_pfn_range(vma,
                          vma->vm_start,
                          pfn,
                          size,
                          vma->vm_page_prot);

    pr_info("remap_pfn_range ret: %d\n", ret);
    return ret;
}

调试错误日志

[28288.407882] memory: 00000000a60b865d
[28288.409341] vma->vm_start: 140257261522944
[28288.409351] vma->vm_end: 140257261527040
[28288.409362] pfn: 406657
[28288.414843] BUG: unable to handle page fault for address: ffffe9ff0b79a488
[28288.414856] #PF: supervisor read access in kernel mode
[28288.414865] #PF: error_code(0x0000) - not-present page
[28288.414873] PGD 0 P4D 0 
[28288.414904] Oops: 0000 [#5] SMP NOPTI

错误原因分析

  1. 参数传递错误:代码中调用vmalloc_to_pfn(k_qp),但实际vmalloc分配的内存指针是memory,传错参数导致获取的pfn对应无效虚拟地址,后续映射逻辑完全错误。
  2. vmalloc地址处理不当:vmalloc_to_pfn要求输入地址必须是vmalloc分配的页对齐地址,若传入非对齐地址或无效地址,会返回错误的pfn值,进而触发页表访问异常。
  3. 页表访问失效:错误地址ffffe9ff0b79a488属于vmalloc地址区间,但对应的页表项未建立,内核访问时触发页错误。

解决方案

一、vmalloc内存的逐页映射方法

vmalloc分配的是离散物理页,需遍历每个页单独映射:

  • 按PAGE_SIZE拆分vmalloc内存区间
  • 用vmalloc_to_page获取对应页的struct page指针
  • 通过page_to_pfn得到页帧号,或直接用vm_insert_page完成映射

二、无需转换物理地址的替代方案

使用vm_insert_page函数,直接将内核struct page关联到用户态VMA,无需手动计算pfn,该函数会自动处理页表建立逻辑,更适合离散物理页的场景。

修复后的代码示例

static int custom_mmap(struct file *filp, struct vm_area_struct *vma)
{
    unsigned long size = vma->vm_end - vma->vm_start;
    unsigned long kernel_addr = (unsigned long)memory; // memory为vmalloc分配的有效指针
    unsigned long offset = 0;
    struct page *page;
    extern size_t alloc_size; // 提前保存的vmalloc分配总大小

    pr_info("vma range: %p - %p, size: %lu\n", vma->vm_start, vma->vm_end, size);
    pr_info("vmalloc addr: %p\n", (void*)kernel_addr);

    // 校验请求大小是否超过内核分配的内存总量
    if (size > alloc_size) {
        pr_err("request size exceeds allocated memory\n");
        return -EINVAL;
    }

    // 逐页映射到用户态VMA
    while (offset < size) {
        page = vmalloc_to_page((void*)(kernel_addr + offset));
        if (!page) {
            pr_err("failed to get page for addr %p\n", (void*)(kernel_addr + offset));
            return -EFAULT;
        }

        if (vm_insert_page(vma, vma->vm_start + offset, page)) {
            pr_err("vm_insert_page failed at offset %lu\n", offset);
            return -EFAULT;
        }

        offset += PAGE_SIZE;
    }

    // 设置VMA属性,允许读写与共享
    vma->vm_flags |= VM_SHARED | VM_MAYREAD | VM_MAYWRITE;
    return 0;
}

关键注意事项

  • 确保memory是vmalloc返回的有效指针,且未被提前释放
  • vmalloc_to_page仅对vmalloc分配的地址有效,不可用于kmalloc或栈内存
  • 用户态调用mmap时,请求大小最好是PAGE_SIZE的整数倍,避免部分页映射的异常
  • 需正确管理内存生命周期,禁止在用户态仍在使用时释放内核侧内存

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 17:15:53