1. Linux内核内存管理基础
在用户态程序中,我们习惯使用malloc/free来动态管理内存,但内核态的内存管理机制完全不同。内核作为操作系统的核心,必须自己管理所有物理内存,同时还要处理各种特殊场景:比如不允许睡眠的原子上下文、需要连续物理页面的DMA操作、不同CPU架构的内存对齐要求等。
内核内存管理最核心的数据结构是struct page。每个物理内存页(通常4KB)都对应一个page结构体,这些结构体存放在mem_map数组中。通过这个基础架构,内核实现了buddy system(伙伴系统)来管理物理页面,以及slab allocator( slab分配器)来高效分配小内存对象。
注意:内核态内存错误往往直接导致oops或panic,不像用户态程序有段错误保护。在内核开发中,内存管理必须格外谨慎。
2. 内核动态内存申请接口解析
2.1 最常用分配函数:kmalloc/kfree
kmalloc是内核中最像malloc的接口,其函数原型为:
c复制void *kmalloc(size_t size, gfp_t flags);
关键参数解析:
- size:申请字节数,实际可能向上对齐到2的幂次
- flags:分配标志位,控制分配行为和内存类型
常用flags组合示例:
c复制GFP_KERNEL // 常规分配,可能睡眠
GFP_ATOMIC // 原子上下文使用,从不睡眠
GFP_DMA // 分配DMA可用内存
实际使用示例:
c复制char *buf = kmalloc(1024, GFP_KERNEL);
if (!buf) {
// 错误处理
}
// 使用buf...
kfree(buf);
2.2 页级分配器:alloc_pages
当需要分配整个内存页时(比如驱动需要DMA缓冲区),应该使用页分配器:
c复制struct page *alloc_pages(gfp_t gfp_mask, unsigned int order);
order参数表示分配2^order个连续物理页。例如order=3表示分配8个页(32KB on 4K pages)。
使用示例:
c复制struct page *page = alloc_pages(GFP_KERNEL, 2);
if (!page) {
// 错误处理
}
void *vaddr = page_address(page);
// 使用vaddr...
__free_pages(page, 2);
2.3 高端内存处理:vmalloc
vmalloc分配虚拟地址连续但物理地址不一定连续的内存,适用于大块内存申请:
c复制void *vmalloc(unsigned long size);
典型使用场景:
- 需要大量连续虚拟地址空间(如模块加载)
- 物理内存碎片化严重时
- 不要求物理连续性的缓冲区
注意:vmalloc分配的内存不能直接用于DMA操作,因为物理页面可能不连续。
