内存分配API
Linux
内核提供了多种内存分配 API
,适应不同的内存分配需求。kmalloc
是用于分配物理连续内存的快速 API
,适合小内存块的分配。vmalloc
用于分配较大的虚拟内存块,适合对物理连续性不要求的场景。kmap
和 kmap_atomic
则用于映射物理内存到内核虚拟地址空间,适用于直接操作物理内存的场景。
kmalloc
kmalloc
是 Linux 内核中最常用的内存分配函数之一,用于分配物理连续的内存区域。
特点
- 物理连续内存:
kmalloc
分配的内存是物理上连续的,这使得它适用于需要直接访问硬件设备的场景(如DMA
操作),以及需要性能优化的场景(例如缓存、数据传输等)。 - 快速分配:由于
kmalloc
使用的是内核的伙伴系统(Buddy System
),其性能非常高效,尤其适合小内存分配。 - 内存大小限制:
kmalloc
支持的最大分配尺寸通常与系统的页面大小(一般为4KB
)以及系统的内存管理有关。kmalloc
分配的内存块通常是以页为单位进行分配的。 - 返回指针:
kmalloc
返回的是直接可访问的虚拟内存地址。
使用示例
#include <linux/slab.h>
void *ptr = kmalloc(1024, GFP_KERNEL);
if (!ptr) {
// 处理分配失败
}
底层实现
kmalloc
使用内核的 伙伴系统(Buddy System
)来分配物理连续内存。它将内存划分为若干个大小为2
的幂次方的内存块,并通过合并和拆分的方式进行内存分配和释放。
vmalloc
vmalloc
用于分配虚拟内存区域,但它分配的内存不要求物理连续。也就是说,vmalloc
分配的内存是虚拟地址上连续的,但其在物理内存中可能是非连续的。
特点
- 虚拟地址连续:
vmalloc
分配的内存区域在虚拟地址空间中是连续的,但在物理内存中不需要是连续的。 - 适用于大内存块:
vmalloc
适合于分配较大内存块(超过kmalloc
支持的大小),尤其是在物理内存无法提供连续的空闲区域时。 - 较慢的分配:由于
vmalloc
使用了页表来映射虚拟地址与物理地址之间的关系,分配速度相对较慢,尤其是在大量内存分配时。 - 适用场景:适用于需要较大内存块且不要求物理连续的情况,如内核模块的缓冲区、大型数据结构等。
使用示例
#include <linux/vmalloc.h>
void *ptr = vmalloc(1024 * 1024); // 分配 1MB 的内存
if (!ptr) {
// 处理分配失败
}
底层实现
vmalloc
使用了 页表映射 的机制,它将虚拟内存地址映射到物理内存页。这些物理页面在物理内存中可能不连续,但虚拟地址是连续的。- 内核将虚拟地址空间划分为多个区域,通过 页表 来管理虚拟地址和物理内存的映射。
kmap / kmap_atomic
kmap
和 kmap_atomic
用于映射页框(Page Frame
)到内核的虚拟地址空间。这两个函数的作用是将物理页面映射为内核空间的虚拟地址,使得内核可以直接访问物理内存中的某个区域。kmap_atomic
是 kmap
的原子版本,通常在中断上下文中使用。
特点
- kmap:将物理页面映射到内核虚拟地址空间中,使内核能够访问物理页面。适用于大部分需要直接访问物理内存的场景。
- kmap_atomic:是
kmap
的原子版本,主要用于中断上下文或禁止页表切换的场景。由于它不会发生上下文切换,因此需要限制每次只能映射少量的内存。 - 内存保护:通过内核页表映射,内存的保护机制依然有效。映射后的虚拟地址可以像普通指针一样使用。
使用示例
#include <linux/highmem.h>
void *virtual_address;
physical_page = ... // 获取物理页面的指针
// 在进程上下文中使用 kmap
virtual_address = kmap(physical_page);
// 在中断上下文中使用 kmap_atomic
virtual_address = kmap_atomic(physical_page, KM_IRQ0);
// 访问完成后,释放映射
kunmap(virtual_address);
kunmap_atomic(virtual_address);
底层实现
kmap
和kmap_atomic
都使用了内核中的 高端内存(High Memory
)区域,通常用于访问那些不直接映射到内核地址空间的物理内存页。它们通过修改页表实现物理页和虚拟地址的映射。kmap_atomic
通常用于上下文切换不可见的场景(例如中断上下文),并使用了内核的原子操作来避免线程切换。
kmalloc 与 vmalloc 的对比
特性 | kmalloc | vmalloc |
---|---|---|
内存分配方式 | 物理连续内存 | 虚拟地址连续,物理地址不连续 |
适用场景 | 小内存块、需要物理连续的场景 | 大内存块、对物理内存不连续不敏感的场景 |
速度 | 较快(基于伙伴系统) | 较慢(使用页表映射) |
分配大小限制 | 通常为一页内存(4KB),最大分配几 MB | 支持较大的内存分配(几 MB 或更多) |
底层实现 | 使用伙伴系统(Buddy System) | 使用页表映射,访问高端内存 |
分配的内存能否直接用于 DMA | 可以 | 不可以(需要通过其他机制映射) |
kmalloc 和 kmap/vmalloc 的选择
- 使用
kmalloc
:当需要快速分配较小、物理连续的内存时,kmalloc
是首选,尤其是当内存分配大小较小(通常为几 KB 或更小)时,kmalloc
性能较高。 - 使用
vmalloc
:当需要分配较大的内存块,且不关心物理地址的连续性时,vmalloc
是合适的选择,尤其是在系统没有足够的物理连续内存时。 - 使用
kmap
或kmap_atomic
:用于在内核空间访问高端内存或者在中断上下文中访问内存。kmap_atomic
适用于不能进行上下文切换的场景。