内存分配API

Linux 内核提供了多种内存分配 API,适应不同的内存分配需求。kmalloc 是用于分配物理连续内存的快速 API,适合小内存块的分配。vmalloc 用于分配较大的虚拟内存块,适合对物理连续性不要求的场景。kmapkmap_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

kmapkmap_atomic 用于映射页框(Page Frame)到内核的虚拟地址空间。这两个函数的作用是将物理页面映射为内核空间的虚拟地址,使得内核可以直接访问物理内存中的某个区域。kmap_atomickmap 的原子版本,通常在中断上下文中使用。

特点

  • 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);

底层实现

  • kmapkmap_atomic 都使用了内核中的 高端内存High Memory)区域,通常用于访问那些不直接映射到内核地址空间的物理内存页。它们通过修改页表实现物理页和虚拟地址的映射。
  • kmap_atomic 通常用于上下文切换不可见的场景(例如中断上下文),并使用了内核的原子操作来避免线程切换。

kmalloc 与 vmalloc 的对比

特性kmallocvmalloc
内存分配方式物理连续内存虚拟地址连续,物理地址不连续
适用场景小内存块、需要物理连续的场景大内存块、对物理内存不连续不敏感的场景
速度较快(基于伙伴系统)较慢(使用页表映射)
分配大小限制通常为一页内存(4KB),最大分配几 MB支持较大的内存分配(几 MB 或更多)
底层实现使用伙伴系统(Buddy System)使用页表映射,访问高端内存
分配的内存能否直接用于 DMA可以不可以(需要通过其他机制映射)

kmalloc 和 kmap/vmalloc 的选择

  • 使用 kmalloc:当需要快速分配较小、物理连续的内存时,kmalloc 是首选,尤其是当内存分配大小较小(通常为几 KB 或更小)时,kmalloc 性能较高。
  • 使用 vmalloc:当需要分配较大的内存块,且不关心物理地址的连续性时,vmalloc 是合适的选择,尤其是在系统没有足够的物理连续内存时。
  • 使用 kmapkmap_atomic:用于在内核空间访问高端内存或者在中断上下文中访问内存。kmap_atomic 适用于不能进行上下文切换的场景。