Linux内核中的内存分配器详解

张开发
2026/4/12 22:06:33 15 分钟阅读

分享文章

Linux内核中的内存分配器详解
Linux内核中的内存分配器详解引言内存分配器是Linux内核中负责管理内存资源的核心组件它为内核和用户空间程序提供内存分配服务。Linux内核使用多种内存分配器来满足不同场景的需求从快速的小内存分配到大型的连续内存分配。本文将深入探讨Linux内核中的内存分配器包括其原理、实现和应用。内存分配器的基本概念1. 内存分配的需求速度快速响应内存分配请求效率减少内存碎片可靠性确保内存分配的可靠性灵活性适应不同大小的内存需求2. 内存分配器的分类SLAB分配器用于小内存分配伙伴系统用于大内存分配VMALLOC用于非连续内存分配CMA用于连续内存分配3. 内存分配的层次结构用户空间 ↓ glibc malloc ↓ sbrk/mmap系统调用 ↓ 内核内存分配器 ↓ 伙伴系统 ↓ 物理内存SLAB分配器1. SLAB的原理SLAB分配器是Linux内核中用于小内存分配的主要分配器它通过缓存对象来提高分配效率。2. SLAB的结构#include linux/slab.h struct kmem_cache { struct kmem_cache_cpu __percpu *cpu_slab; size_t object_size; size_t size; u32 align; u32 flags; const char *name; struct list_head list; // 其他字段... }; struct slab { struct list_head list; unsigned long colouroff; void *s_mem; unsigned int inuse; kmem_bufctl_t free; // 其他字段... };3. SLAB的操作#include linux/slab.h // 创建slab缓存 struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags, void (*ctor)(void *)); // 销毁slab缓存 void kmem_cache_destroy(struct kmem_cache *s); // 分配对象 void *kmem_cache_alloc(struct kmem_cache *s, gfp_t gfpflags); // 释放对象 void kmem_cache_free(struct kmem_cache *s, void *objp); // 通用内存分配 void *kmalloc(size_t size, gfp_t flags); void kfree(const void *objp);4. SLAB的示例#include linux/module.h #include linux/kernel.h #include linux/init.h #include linux/slab.h struct my_struct { int value; char name[32]; }; static struct kmem_cache *my_cachep; static int __init slab_demo_init(void) { struct my_struct *obj; // 创建slab缓存 my_cachep kmem_cache_create(my_struct, sizeof(struct my_struct), 0, SLAB_HWCACHE_ALIGN, NULL); if (!my_cachep) { printk(KERN_ERR Failed to create slab cache\n); return -ENOMEM; } // 分配对象 obj kmem_cache_alloc(my_cachep, GFP_KERNEL); if (!obj) { printk(KERN_ERR Failed to allocate object\n); kmem_cache_destroy(my_cachep); return -ENOMEM; } // 使用对象 obj-value 42; snprintf(obj-name, sizeof(obj-name), test); printk(KERN_INFO Allocated object: value%d, name%s\n, obj-value, obj-name); // 释放对象 kmem_cache_free(my_cachep, obj); return 0; } static void __exit slab_demo_exit(void) { if (my_cachep) { kmem_cache_destroy(my_cachep); } printk(KERN_INFO Slab demo exited\n); } module_init(slab_demo_init); module_exit(slab_demo_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(Demo); MODULE_DESCRIPTION(SLAB allocator demo);伙伴系统1. 伙伴系统的原理伙伴系统是Linux内核中用于管理物理内存的分配器它通过将内存块组织成不同大小的伙伴对来减少内存碎片。2. 伙伴系统的结构#include linux/mmzone.h struct free_area { struct list_head free_list[MIGRATE_TYPES]; unsigned long nr_free; }; struct zone { struct free_area free_area[MAX_ORDER]; unsigned long zone_start_pfn; unsigned long managed_pages; unsigned long spanned_pages; unsigned long present_pages; // 其他字段... };3. 伙伴系统的操作#include linux/gfp.h // 分配页面 struct page *alloc_pages(gfp_t gfp_mask, unsigned int order); // 分配单个页面 struct page *alloc_page(gfp_t gfp_mask); // 分配连续内存 void *__get_free_pages(gfp_t gfp_mask, unsigned int order); // 释放页面 void __free_pages(struct page *page, unsigned int order); // 释放单个页面 void __free_page(struct page *page);4. 伙伴系统的示例#include linux/module.h #include linux/kernel.h #include linux/init.h #include linux/gfp.h static struct page *pages; static int order 2; // 4 pages (16KB) static int __init buddy_demo_init(void) { void *addr; // 分配页面 pages alloc_pages(GFP_KERNEL, order); if (!pages) { printk(KERN_ERR Failed to allocate pages\n); return -ENOMEM; } // 获取虚拟地址 addr page_address(pages); printk(KERN_INFO Allocated %d pages at %p\n, 1 order, addr); // 使用内存 memset(addr, 0, PAGE_SIZE order); *(int *)addr 42; printk(KERN_INFO Value at %p: %d\n, addr, *(int *)addr); return 0; } static void __exit buddy_demo_exit(void) { if (pages) { __free_pages(pages, order); printk(KERN_INFO Freed %d pages\n, 1 order); } printk(KERN_INFO Buddy demo exited\n); } module_init(buddy_demo_init); module_exit(buddy_demo_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(Demo); MODULE_DESCRIPTION(Buddy system demo);VMALLOC1. VMALLOC的原理VMALLOC用于分配非连续的虚拟内存适用于需要较大内存但不需要物理连续的场景。2. VMALLOC的操作#include linux/vmalloc.h // 分配虚拟内存 void *vmalloc(unsigned long size); // 分配对齐的虚拟内存 void *vzalloc(unsigned long size); // 分配可执行的虚拟内存 void *vmalloc_exec(unsigned long size); // 释放虚拟内存 void vfree(const void *addr); // 分配大内存 void *vmalloc_32(unsigned long size);3. VMALLOC的示例#include linux/module.h #include linux/kernel.h #include linux/init.h #include linux/vmalloc.h static void *vmalloc_addr; static unsigned long size 1024 * 1024; // 1MB static int __init vmalloc_demo_init(void) { // 分配虚拟内存 vmalloc_addr vmalloc(size); if (!vmalloc_addr) { printk(KERN_ERR Failed to allocate vmalloc memory\n); return -ENOMEM; } printk(KERN_INFO Allocated %lu bytes at %p\n, size, vmalloc_addr); // 使用内存 memset(vmalloc_addr, 0, size); *(int *)vmalloc_addr 42; printk(KERN_INFO Value at %p: %d\n, vmalloc_addr, *(int *)vmalloc_addr); return 0; } static void __exit vmalloc_demo_exit(void) { if (vmalloc_addr) { vfree(vmalloc_addr); printk(KERN_INFO Freed vmalloc memory\n); } printk(KERN_INFO VMALLOC demo exited\n); } module_init(vmalloc_demo_init); module_exit(vmalloc_demo_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(Demo); MODULE_DESCRIPTION(VMALLOC demo);CMAContiguous Memory Allocator1. CMA的原理CMA用于分配物理连续的内存适用于需要连续内存的设备驱动。2. CMA的配置# 配置CMA echo cma256M /boot/cmdline.txt # 查看CMA信息 cat /proc/meminfo | grep CMA3. CMA的操作#include linux/dma-contiguous.h // 分配CMA内存 struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp); // 释放CMA内存 void dma_free_contiguous(struct device *dev, struct page *page, size_t size); // 预留CMA内存 struct cma *cma_reserve(unsigned long size); // 分配CMA内存 struct page *cma_alloc(struct cma *cma, size_t count, unsigned int align); // 释放CMA内存 bool cma_release(struct cma *cma, const struct page *page, unsigned int count);4. CMA的示例#include linux/module.h #include linux/kernel.h #include linux/init.h #include linux/dma-contiguous.h static struct page *cma_pages; static size_t size 16 * 1024 * 1024; // 16MB static int __init cma_demo_init(void) { void *addr; // 分配CMA内存 cma_pages dma_alloc_contiguous(NULL, size, GFP_KERNEL); if (!cma_pages) { printk(KERN_ERR Failed to allocate CMA memory\n); return -ENOMEM; } // 获取虚拟地址 addr page_address(cma_pages); printk(KERN_INFO Allocated %zu bytes CMA memory at %p\n, size, addr); // 使用内存 memset(addr, 0, size); *(int *)addr 42; printk(KERN_INFO Value at %p: %d\n, addr, *(int *)addr); return 0; } static void __exit cma_demo_exit(void) { if (cma_pages) { dma_free_contiguous(NULL, cma_pages, size); printk(KERN_INFO Freed CMA memory\n); } printk(KERN_INFO CMA demo exited\n); } module_init(cma_demo_init); module_exit(cma_demo_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(Demo); MODULE_DESCRIPTION(CMA demo);内存分配的调优1. 分配标志的选择标志描述适用场景GFP_KERNEL内核空间分配可睡眠普通内核内存分配GFP_ATOMIC原子分配不可睡眠中断上下文GFP_DMA分配DMA可用内存设备驱动GFP_HIGHUSER分配高端内存大内存分配GFP_NOIO不允许IO操作IO路径GFP_NOWAIT不等待内存回收紧急分配2. 内存分配的最佳实践小内存使用kmalloc或slab缓存大内存使用alloc_pages或vmalloc连续内存使用CMA或alloc_pages设备内存使用dma_alloc_coherent3. 内存分配的性能优化预分配提前分配内存避免运行时分配缓存使用slab缓存提高分配效率批量分配减少分配次数内存池为特定场景创建内存池实际案例分析1. 设备驱动内存分配#include linux/module.h #include linux/kernel.h #include linux/init.h #include linux/fs.h #include linux/cdev.h #include linux/slab.h struct my_device { struct cdev cdev; char *buffer; size_t size; }; static struct my_device *dev; static int __init my_driver_init(void) { // 分配设备结构 dev kmalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { return -ENOMEM; } // 分配缓冲区 dev-size 4096; dev-buffer kmalloc(dev-size, GFP_KERNEL); if (!dev-buffer) { kfree(dev); return -ENOMEM; } printk(KERN_INFO Driver initialized\n); return 0; } static void __exit my_driver_exit(void) { if (dev) { if (dev-buffer) { kfree(dev-buffer); } kfree(dev); } printk(KERN_INFO Driver exited\n); } module_init(my_driver_init); module_exit(my_driver_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(Demo); MODULE_DESCRIPTION(Memory allocation demo);2. 内存池的使用#include linux/module.h #include linux/kernel.h #include linux/init.h #include linux/mempool.h #define POOL_SIZE 10 #define OBJ_SIZE 128 static mempool_t *mempool; static void *mempool_alloc(gfp_t gfp_mask, void *data) { return kmalloc(OBJ_SIZE, gfp_mask); } static void mempool_free(void *element, void *data) { kfree(element); } static int __init mempool_demo_init(void) { void *obj; // 创建内存池 mempool mempool_create(POOL_SIZE, mempool_alloc, mempool_free, NULL); if (!mempool) { printk(KERN_ERR Failed to create mempool\n); return -ENOMEM; } // 分配对象 obj mempool_alloc(GFP_KERNEL, mempool); if (!obj) { mempool_destroy(mempool); return -ENOMEM; } printk(KERN_INFO Allocated object from mempool\n); // 释放对象 mempool_free(obj, mempool); return 0; } static void __exit mempool_demo_exit(void) { if (mempool) { mempool_destroy(mempool); } printk(KERN_INFO Mempool demo exited\n); } module_init(mempool_demo_init); module_exit(mempool_demo_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(Demo); MODULE_DESCRIPTION(Mempool demo);结论内存分配器是Linux内核中管理内存资源的核心组件它为内核和用户空间程序提供了高效的内存分配服务。从SLAB分配器到伙伴系统从VMALLOC到CMALinux内核提供了多种内存分配机制来满足不同场景的需求。理解这些内存分配器的原理和使用方法对于内核开发和系统调优都有重要意义。

更多文章