Linux 内核内存分配主要分为页级分配物理连续和slab 分配字节级基于缓存前者适合大块内存后者适合小内存对象。页级分配 API物理连续内存基于伙伴系统buddy system以页page为单位分配物理连续的内存核心函数在linux/gfp.h和linux/mm.h中。函数功能典型用法alloc_pages(gfp_mask, order)分配2^order个连续物理页分配大块连续内存如 DMA 缓冲区__get_free_pages(gfp_mask, order)同上但返回页的虚拟地址内核线性区更易用的页分配无需手动映射get_free_page(gfp_mask)等价于__get_free_pages(gfp_mask, 0)分配 1 页分配单页内存free_pages(addr, order)释放页级分配的内存对应__get_free_pages/alloc_pages关键参数gfp_mask分配掩码gfp_mask 决定分配行为核心掩码组合GFP_KERNEL内核常规分配允许睡眠会触发页回收 / 交换最常用进程上下文使用。GFP_ATOMIC原子分配不允许睡眠中断上下文 / 持有自旋锁时使用分配失败概率更高。GFP_DMA分配适合 DMA 的内存低地址如 16MB适配老式硬件。GFP_DMA32分配 32 位地址可访问的 DMA 内存。__GFP_ZERO分配后将内存清零避免脏数据泄露。Slab 分配 API字节级内存针对小内存对象如内核结构体的高效分配基于 slab/slub/slob 分配器主流是 slub核心函数在linux/slab.h中优先使用比页分配更高效减少内存碎片。函数功能适用场景kmalloc(size, gfp_mask)分配字节级内存大小为 2 的幂通用小内存分配如结构体、缓冲区kzalloc(size, gfp_mask)等价于kmalloc memset(0)需要清零的内存避免野指针kfree(ptr)释放 kmalloc/kzalloc 分配的内存对应上述分配函数kcalloc(n, size, gfp_mask)分配 n 个 size 大小的内存并清零数组类内存分配krealloc(ptr, new_size, gfp_mask)重新分配内存类似用户态 realloc动态调整内存大小自定义 Slab 缓存接口高性能场景当需要频繁创建 / 销毁同一类型的对象如驱动中的设备结构体、网络报文结构体时自定义 Slab 缓存kmem_cache能减少内存碎片、提升性能预分配 / 复用对象。核心接口接口函数功能说明关键参数 / 注意事项struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags, void (*ctor)(void *))创建自定义 Slab 缓存-name缓存名称用于调试如/sys/kernel/slab/name-size单个对象大小-align对象对齐要求0 为默认对齐-flags缓存标志如SLAB_HWCACHE_ALIGN按缓存行对齐-ctor对象构造函数初始化可选- 返回值成功返回缓存指针失败返回NULLvoid *kmem_cache_alloc(struct kmem_cache *s, gfp_t gfp_mask)从自定义缓存分配一个对象类似kmalloc但从专属缓存分配void kmem_cache_free(struct kmem_cache *s, void *obj)释放自定义缓存中的对象必须和kmem_cache_alloc配对void kmem_cache_destroy(struct kmem_cache *s)销毁自定义缓存- 必须确保缓存中无未释放的对象- 模块退出前必须调用自定义缓存标志常用SLAB_HWCACHE_ALIGN对象按 CPU 缓存行对齐减少缓存颠簸提升性能。SLAB_POISON用特殊值填充空闲对象调试内存越界。SLAB_RED_ZONE在对象前后加保护区域检测越界访问。SLAB_PANIC创建缓存失败时触发内核 panic关键缓存。专用场景分配 API1栈内存内核栈内核栈大小固定通常 8K/16K禁止在栈上分配大数组如char buf[1024*1024]否则会导致栈溢出内核 panic。小对象1K可直接用栈大对象必须用kmalloc/alloc_pages。2高端内存分配highmem对于 32 位系统内核线性区3G-4G不足以映射所有物理内存高端内存highmem需要特殊映射alloc_pages(GFP_HIGHUSER, order)分配高端内存页。kmap(page)将 highmem 页映射到内核线性区临时映射禁止睡眠。kunmap(page)解除映射。64 位系统无 highmem 限制可忽略此部分。3DMA 内存分配针对设备 DMA 场景需要物理连续且地址可被设备访问dma_alloc_coherent(dev, size, dma_handle, gfp_mask)分配 DMA 一致性内存CPU 和设备缓存一致返回虚拟地址和 DMA 物理地址。dma_free_coherent(dev, size, vaddr, dma_handle)释放 DMA 内存。内存释放的核心规则配对使用分配和释放函数必须匹配如kmalloc↔kfree__get_free_pages↔free_pages否则会导致内存泄漏或双重释放内核 panic。空指针安全kfree(NULL)/free_pages(NULL, order)是安全的无需额外判空。禁止重复释放释放后的指针必须置 NULL如kfree(data); data NULL;避免野指针。上下文限制GFP_KERNEL仅能在进程上下文使用允许睡眠。GFP_ATOMIC可在中断上下文 / 自旋锁持有期间使用不允许睡眠。内存调试 API内核提供了一系列 Slab 调试接口帮助检测内存泄漏、越界、重复释放等问题需开启内核配置CONFIG_SLUB_DEBUG/CONFIG_DEBUG_KMEMLEAK。接口 / 工具功能说明使用方式kmalloc_track_caller(size, gfp_mask)跟踪内存分配的调用栈替代kmalloc调试时定位泄漏源头kmem_cache_shrink(struct kmem_cache *s)收缩缓存释放空闲的 Slab 页主动回收缓存中的空闲内存slabinfo命令查看系统 Slab 缓存状态终端执行slabinfo或cat /proc/slabinfokmemleak检测内存泄漏开启后通过echo scan /sys/kernel/debug/kmemleak触发扫描WARN_ON(kmem_cache_ptr_check(s, obj))检查对象是否属于指定缓存调试时验证释放的对象是否合法kmemleakkmemleak是 Linux 内核内置的内存泄漏检测器专为内核态内存泄漏问题设计针对kmalloc/kzalloc/kmem_cache_alloc/alloc_pages等接口分配的内存。它通过跟踪内核内存的分配 / 引用关系识别未释放且无有效引用的内存块是内核开发 / 调试的核心工具。核心原理kmemleak采用「引用追踪」机制当内核分配内存时kmemleak记录该内存块的地址、大小、调用栈等信息并标记为「疑似泄漏」当内存被赋值给全局变量、结构体成员、栈变量等有效引用时kmemleak清除其「疑似泄漏」标记触发扫描时kmemleak遍历所有未释放的内存块无有效引用的块会被判定为「内存泄漏」并输出报告。启用条件1. 内核编译配置需开启以下配置基于 Linux 5.x 及以上CONFIG_DEBUG_KMEMLEAKy # 核心开关 CONFIG_DEBUG_KMEMLEAK_EARLY_LOGy # 早期启动日志可选 CONFIG_DEBUG_KMEMLEAK_DEFAULT_ONy # 默认启用可选 CONFIG_DEBUG_KMEMLEAK_MEM_POOL_SIZE16384 # 内存池大小按需调整编译内核后kmemleak会默认启用可通过启动参数关闭。2. 内核启动参数启用kmemleakon默认禁用kmemleakoff调试完成后关闭减少性能开销调试模式kmemleakdebug输出更详细的追踪日志。核心操作用户态交互kmemleak的交互入口是/sys/kernel/debug/kmemleak需挂载 debugfs核心操作步骤1. 挂载 debugfs必做mount -t debugfs none /sys/kernel/debug2. 核心命令通过 echo 操作 kmemleak 文件命令功能echo scan /sys/kernel/debug/kmemleak触发内存泄漏扫描手动触发核心操作echo clear /sys/kernel/debug/kmemleak清除历史泄漏报告重置扫描状态echo dump /sys/kernel/debug/kmemleak导出当前所有疑似泄漏的内存块信息echo off /sys/kernel/debug/kmemleak关闭 kmemleak运行时禁用echo on /sys/kernel/debug/kmemleak重新启用 kmemleakecho loglevel /sys/kernel/debug/kmemleak设置日志级别0 静默1 错误2 警告3 信息3. 查看泄漏报告扫描完成后泄漏信息会输出到内核日志dmesg或/var/log/kern.log直接读取cat /sys/kernel/debug/kmemleak。关键要点性能开销kmemleak会增加内核内存开销约 1-5%和运行时性能损耗仅用于调试生产环境需关闭误判可能部分内存引用如汇编层、硬件寄存器指向的内存无法被自动检测可能导致误判需结合业务逻辑分析扫描时机建议在模块加载 / 卸载后、系统运行一段时间后触发扫描避免 “临时未引用” 的误报不检测的内存kmemleak仅跟踪内核核心分配接口对vmalloc/ioremap等分配的内存支持有限。kmemleak是内核内存泄漏调试的核心工具需通过内核编译配置启用依赖 debugfs 交互核心操作是echo scan /sys/kernel/debug/kmemleak触发扫描通过内核日志 //sys/kernel/debug/kmemleak查看泄漏报告关键技巧用kmemleak_ignore忽略已知假泄漏用调用栈定位泄漏代码行修复后需验证扫描结果。Slab 接口使用核心规则上下文匹配进程上下文如驱动 probe 函数用GFP_KERNEL允许睡眠分配成功率高。中断上下文 / 自旋锁持有期间用GFP_ATOMIC不允许睡眠失败概率高。配对释放kmalloc/kzalloc→kfreekmem_cache_alloc→kmem_cache_free 最终kmem_cache_destroy。避免大对象Slab 适合小对象 PAGE_SIZE通常 4K/8K大块内存用alloc_pages。调试优先开发阶段开启SLAB_POISON/SLAB_RED_ZONE提前发现越界问题。总结核心分配方式小内存PAGE_SIZE用kmalloc/kzallocslab大块连续内存用alloc_pages/__get_free_pages伙伴系统DMA 场景用dma_alloc_coherent。关键参数gfp_mask决定分配上下文进程上下文用GFP_KERNEL中断 / 原子上下文用GFP_ATOMIC。安全规则分配 - 释放必须配对释放后指针置 NULL禁止栈上分配大内存避免越界 / 重复释放。用场景优先使用kzalloc/kmallockfree简单高效满足绝大多数小内存分配需求。高性能场景频繁创建同类型对象时用kmem_cache_create自定义缓存减少内存碎片和初始化开销。核心规则分配 / 释放函数严格配对根据上下文选择正确的gfp_mask调试阶段启用 Slab 调试功能。