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

用一个代码案例详解介绍vmalloc函数的功能和作用

vmalloc 函数详解:代码案例与功能分析

功能与作用

vmalloc 是 Linux 内核中用于分配虚拟地址连续但物理地址不连续的内存区域的函数:

 

虚拟地址连续:用户/内核可通过单一指针访问整个内存块

物理地址不连续:实际内存由多个分散的物理页帧组成

大内存支持:可分配远大于 kmalloc 的内存块(如 1GB)

避免碎片:解决物理内存碎片导致的分配失败问题

核心特点

特性 说明

内存来源 通过 alloc_page() 从伙伴系统获取多个分散的物理页帧

地址映射 修改页表将分散物理页映射到连续的虚拟地址区域(VMALLOC_START~VMALLOC_END)

性能开销 较高(需构建页表项 + TLB 刷新)

适用场景 内核模块、大型缓冲区、文件系统缓存等无需物理连续性的场景

禁止场景 DMA操作(硬件需要物理连续内存)

代码案例:内核模块演示 vmalloc 使用

#include <linux/init.h>

#include <linux/module.h>

#include <linux/vmalloc.h>

#include <linux/mm.h>

#include <linux/slab.h>

 

#define MEM_SIZE (5 * 1024 * 1024) // 分配5MB内存

 

static char *vmalloc_ptr = NULL;

static char *kmalloc_ptr = NULL;

 

static void print_mem_info(const char *prefix, void *ptr, size_t size)

{

    struct page *page;

    unsigned long virt_start = (unsigned long)ptr;

    unsigned long virt_end = virt_start + size;

    

    printk(KERN_INFO "%s: 虚拟地址范围: [%pK - %pK]\n", 

           prefix, ptr, ptr + size - 1);

 

    // 遍历虚拟地址对应的物理页

    for (; virt_start < virt_end; virt_start += PAGE_SIZE) {

        page = vmalloc_to_page((void *)virt_start);

        if (page) {

            printk(KERN_CONT " 虚拟地址 %pK -> 物理页帧: 0x%lx\n",

                   (void *)virt_start, page_to_pfn(page) << PAGE_SHIFT);

        }

    }

}

 

static int __init mem_test_init(void)

{

    // 1. vmalloc分配 (物理不连续)

    vmalloc_ptr = vmalloc(MEM_SIZE);

    if (!vmalloc_ptr) {

        printk(KERN_ERR "vmalloc 分配失败!\n");

        return -ENOMEM;

    }

 

    // 2. kmalloc分配 (物理连续,对比用)

    kmalloc_ptr = kmalloc(MEM_SIZE / 10, GFP_KERNEL); // 分配500KB对比

    if (!kmalloc_ptr) {

        vfree(vmalloc_ptr);

        printk(KERN_ERR "kmalloc 分配失败!\n");

        return -ENOMEM;

    }

 

    // 打印内存信息

    print_mem_info("vmalloc", vmalloc_ptr, MEM_SIZE);

    print_mem_info("kmalloc", kmalloc_ptr, MEM_SIZE / 10);

 

    // 3. 测试内存访问

    memset(vmalloc_ptr, 0xAA, MEM_SIZE); // 成功写入

    memset(kmalloc_ptr, 0xBB, MEM_SIZE / 10);

 

    printk(KERN_INFO "内存访问测试完成 (无崩溃即成功)\n");

    return 0;

}

 

static void __exit mem_test_exit(void)

{

    if (vmalloc_ptr) {

        vfree(vmalloc_ptr); // 释放vmalloc内存

        printk(KERN_INFO "释放vmalloc内存");

    }

    if (kmalloc_ptr) {

        kfree(kmalloc_ptr); // 释放kmalloc内存

        printk(KERN_INFO "释放kmalloc内存");

    }

}

 

module_init(mem_test_init);

module_exit(mem_test_exit);

MODULE_LICENSE("GPL");

代码解析与输出分析

1. 内存分配

vmalloc_ptr = vmalloc(5 * 1024 * 1024); // 分配5MB

kmalloc_ptr = kmalloc(500 * 1024, GFP_KERNEL); // 分配500KB对比

vmalloc 轻松分配 5MB 大内存

kmalloc 分配 500KB(仅作物理连续性对比)

2. 地址信息输出

# dmesg 输出示例

vmalloc: 虚拟地址范围: [0xffff0000c0000000 - 0xffff0000c04fffff]

   虚拟地址 0xffff0000c0000000 -> 物理页帧: 0x7f0000

   虚拟地址 0xffff0000c0001000 -> 物理页帧: 0x324000

   虚拟地址 0xffff0000c0002000 -> 物理页帧: 0x88a000 # 物理地址不连续!

   ... (其他分散页帧)

 

kmalloc: 虚拟地址范围: [0xffff80001134a000 - 0xffff8000113c9fff]

   虚拟地址 0xffff80001134a000 -> 物理页帧: 0x134a000

   虚拟地址 0xffff80001134b000 -> 物理页帧: 0x134b000

   虚拟地址 0xffff80001134c000 -> 物理页帧: 0x134c000 # 物理地址连续!

关键观察

虚拟地址连续性:

两者虚拟地址均连续(单一指针访问整个区域)

物理地址分布:

vmalloc:各页帧物理地址无规律(如 0x7f0000 → 0x324000 → 0x88a000)

kmalloc:物理地址连续递增(0x134a000 → 0x134b000 → 0x134c000)

vmalloc 内部机制图解

 

典型应用场景

内核模块加载

加载大型内核模块(如文件系统驱动)时使用 vmalloc 分配代码/数据空间

 

文件系统缓存

EXT4/Btrfs 等文件系统用 vmalloc 管理大型目录项缓存

 

网络协议栈

分配大数据包缓冲区(如大于 PAGE_SIZE 的 SKB)

 

动态内存映射

实现用户空间的 mmap 系统调用(如 remap_vmalloc_range)

 

重要注意事项

禁止中断上下文使用

vmalloc 可能睡眠(调用 alloc_page()),不能在中断处理程序中使用

 

性能敏感场景慎用

频繁访问时 TLB Miss 率高,性能显著低于 kmalloc

 

特殊内存特性

默认分配 GFP_KERNEL 内存,不可用于原子上下文

 

NUMA系统优化

使用 vmalloc_node 可指定内存所在的 NUMA 节点

 

通过此案例可清晰理解 vmalloc 如何在内核中构建"虚拟连续但物理分散"的内存区域,及其解决大内存分配和碎片问题的核心价值。

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

相关文章:

  • 权限分级看板管理:实时数据驱动决策的关键安全基石
  • 奇异值分解(singular value decomposition,SVD)
  • 笔试——Day2
  • 单细胞入门(2)-经典案例分析
  • EPLAN 电气制图(六):结构盒与设备管理器核心概念(基础知识选看)
  • 脑电分析入门指南:信号处理、特征提取与机器学习
  • python 在运行时没有加载修改后的版本
  • windows server2019安全修复
  • 数据结构——深度优先搜索与广度优先搜索的实现
  • STM32-待机唤醒实验
  • 学习笔记(30):matplotlib绘制简单图表-绘制正弦波
  • Python的标准库之时间库(小白五分钟从入门到精通)
  • 【Netty+WebSocket详解】WebSocket全双工通信与Netty的高效结合与实战
  • 循环神经网络详解
  • cherryStudio electron因为环境问题无法安装解决方法或打包失败解决方法
  • NLP自然语言处理04 transformer架构模拟实现
  • Git版本控制完全指南:从入门到实战(简单版)
  • 【02】MFC入门到精通——MFC 手动添加创建新的对话框模板
  • 【PyTorch】PyTorch中torch.nn模块的全连接层
  • C++每日刷题 day2025.7.09
  • 备受期待的 MMORPG 游戏《侍魂R》移动端现已上线 Sui
  • RK3588 buildroot 解决软件包无法下载
  • 用户查询优惠券之缓存击穿
  • RAC-CELL(小区)处理
  • Ubuntu连接不上网络问题(Network is unreachable)
  • 国产航顺HK32F030M: 串口调试debug,重定向c库函数printf到串口,重定向后可使用printf函数
  • 记一次接口优化历程 CountDownLatch
  • C语言模块化编程思维以及直流电机控制(第四天)
  • 深度学习——损失函数
  • 【使用Flask基于PaddleOCR3.0开发一个接口 调用时报错RuntimeError: std::exception】