Linux vmap
Linux
Linux内核vmap接口用法及使用注意事项,提供一个demo
Linux内核vmap接口详解
接口功能
vmap()
用于将一组物理不连续的内存页映射到连续的虚拟地址空间,常用于需要大块连续虚拟地址但物理内存分散的场景(如DMA缓冲区、文件系统缓存等)。
核心函数
1 |
|
参数说明
- pages: 指向
struct page*
数组的指针 - count: 要映射的页数
- flags: 映射标志(常用
VM_MAP | VM_IOREMAP
) - prot: 页面保护标志(如
PAGE_KERNEL
)
使用注意事项
物理页管理
需预先分配好物理页(如alloc_page()
或kmalloc()
),调用方负责释放映射生命周期
- 映射仅在当前上下文有效(不可跨进程)
- 长期持有可能导致vmalloc空间碎片
内存类型
支持普通内存和高端内存(HIGHMEM)错误处理
vmap()
失败返回NULL
,必须检查返回值性能影响
频繁映射/解除映射影响性能,适合静态场景并发安全
非原子操作,需同步机制保护共享访问
完整Demo代码
1 |
|
关键操作流程
分配物理页数组
kmalloc_array()
分配页指针数组获取物理内存
alloc_page()
为每个元素分配物理页建立虚拟映射
vmap()
创建连续虚拟地址映射使用映射内存
通过返回的虚拟地址访问内存清理资源
vunmap()
→ 释放物理页 → 释放指针数组
常见问题排查
映射失败:
- 检查
vmalloc
空间是否耗尽:grep VmallocUsed /proc/meminfo
- 减少映射页数或重新编译内核增大
vmalloc
空间
- 检查
内存泄漏:
- 确保每次
vmap()
都有对应的vunmap()
- 使用
kmemleak
工具检测未释放内存
- 确保每次
非法访问:
- 不要跨模块传递
vaddr
指针 - 访问范围勿超过
NUM_PAGES * PAGE_SIZE
- 不要跨模块传递
性能提示:对于频繁访问的场景,考虑改用
kmalloc()
获取物理连续内存,避免映射开销。
Linux kmalloc接口用法
Linux kmalloc 接口详解
kmalloc
是 Linux 内核中最常用的内存分配接口之一,用于分配物理地址连续的内核空间内存。
核心特性
- 物理连续性:分配的页帧在物理内存中连续
- 大小限制:最大分配尺寸由系统配置决定(通常 4KB-4MB)
- 高效性:比
vmalloc
更快(无页表操作) - 适用场景:DMA 操作、硬件缓冲区等需要物理连续性的场景
函数原型
1 |
|
参数说明
- size:请求的字节数
- flags:分配控制标志(关键参数!)
- objp:要释放的内存指针
常用 GFP 标志(Get Free Page)
标志 | 适用场景 | 特性 |
---|---|---|
GFP_KERNEL |
进程上下文 | 可能睡眠,最常用 |
GFP_ATOMIC |
中断/原子上下文 | 永不睡眠,紧急分配 |
GFP_DMA |
DMA 操作 | 从 ZONE_DMA 分配 |
GFP_HIGHUSER |
用户空间映射 | 高端内存分配 |
GFP_ZERO |
需要清零内存 | 额外初始化开销 |
使用注意事项
上下文匹配
- 进程上下文:优先用
GFP_KERNEL
- 中断上下文:必须用
GFP_ATOMIC
- 进程上下文:优先用
大小限制
- 最大分配尺寸:
KMALLOC_MAX_SIZE
(通常 4MB) - 小内存(< PAGE_SIZE)使用 slab 分配器
- 最大分配尺寸:
内存对齐
- 默认按
ARCH_KMALLOC_MINALIGN
对齐 - 特殊对齐需求用
kmem_cache_alloc
- 默认按
内存泄漏检测
- 使用
kmemleak
工具检测未释放内存
- 使用
错误处理
- 必须检查返回的指针是否为
NULL
- 必须检查返回的指针是否为
完整 Demo 代码
1 |
|
中断上下文示例
1 | // 在中断处理函数中的使用 |
最佳实践指南
分配大小优化
1
2// 避免碎片化:优先使用2的幂次大小
buffer = kmalloc(512, GFP_KERNEL); // 优于 kmalloc(500, GFP_KERNEL)内存初始化
1
2// 需要清零的内存:
buffer = kzalloc(size, GFP_KERNEL); // 等效 kmalloc + memset大内存分配
1
2
3
4// 超过 KMALLOC_MAX_SIZE 时使用页分配器
if (size > KMALLOC_MAX_SIZE) {
pages = alloc_pages(GFP_KERNEL, get_order(size));
}DMA 内存分配
1
2// 需要 DMA 访问的内存:
dma_buf = kmalloc(DMA_BUF_SIZE, GFP_KERNEL | GFP_DMA);调试技巧
1
2// 检查分配大小限制
pr_info("Max kmalloc size: %zu\n", KMALLOC_MAX_SIZE);
常见错误排查
内核崩溃 (Oops)
- 检查是否在中断上下文中使用了
GFP_KERNEL
- 验证是否访问了已释放的内存
- 检查是否在中断上下文中使用了
分配失败
- 检查
/proc/buddyinfo
内存碎片情况 - 尝试减少分配大小或使用
vmalloc
- 检查
内存损坏
- 使用
KASAN
工具检测越界访问 - 检查是否有重复释放操作
- 使用
性能问题
- 频繁小内存分配/释放考虑使用内存池
1
2// 创建内存池
mempool_t *pool = mempool_create_kmalloc_pool(min_nr, size);
- 频繁小内存分配/释放考虑使用内存池
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Helloeuler!