当前位置: 首页 > news >正文

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

VMALLOC

1. 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");

CMA(Contiguous Memory Allocator)

1. CMA的原理

CMA用于分配物理连续的内存,适用于需要连续内存的设备驱动。

2. CMA的配置

# 配置CMA echo "cma=256M" >> /boot/cmdline.txt # 查看CMA信息 cat /proc/meminfo | grep CMA

3. 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_coherent

3. 内存分配的性能优化

  • 预分配:提前分配内存,避免运行时分配
  • 缓存:使用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到CMA,Linux内核提供了多种内存分配机制来满足不同场景的需求。理解这些内存分配器的原理和使用方法,对于内核开发和系统调优都有重要意义。

http://www.jsqmd.com/news/630944/

相关文章:

  • 专业的东莞geo优化哪个好推荐 - 企业推荐官【官方】
  • SolidWorks2020安装与破解全流程详解
  • Win11Debloat终极指南:免费Windows系统优化工具完整教程
  • Dify平台快速部署Qwen3-ASR-1.7B语音识别模型指南
  • 告别硬编码!用Go的expr表达式引擎5分钟搞定电商促销规则动态配置
  • Spring Cloud进阶--分布式权限校验OAuth写
  • VideoCaptioner:开源AI字幕工具架构解析与技术实现指南
  • VCSA 8.0.3部署后必做的5件事:从SFTP自动备份到关闭密码策略
  • 记一次综合型流量分析 | 添柴不加火滦
  • 东莞geo优化公司找哪家 - 企业推荐官【官方】
  • QKeyMapper终极指南:无需重启Windows,即时自定义你的按键布局
  • .NET 诊断技巧 | 日志框架原理、手写日志框架学习谘
  • FreakStudio郊
  • 信贷风控实战——如何用MOB和Vintage分析资产质量?
  • 第三章
  • Langchain实战:如何用ChatGLM-4搭建你的第一个AI对话机器人(附完整代码)
  • AI开发-python-langchain框架(--并行流程 )颗
  • SQL如何实现同比环比增长率计算_通过LAG函数与聚合计算
  • 如何3分钟快速配置Android开发环境:智能驱动安装终极指南
  • 2026年广东选有机肥,广正丰性价比首选别错过! - 企业推荐官【官方】
  • 从NOJ到算法实战:一份西工大编程训练题的解题思路与代码精讲
  • c语言的基础知识点
  • 八大网盘直链获取工具:告别限速,拥抱高速下载体验
  • AudioSeal Pixel Studio一文详解:AudioSeal抗重采样/变速/噪声叠加鲁棒性测试
  • Linux内核中的系统调用机制详解
  • 在 Go 语言中声明包级全局 map 的正确方式
  • 市场正规的东莞geo优化公司哪个好 - 企业推荐官【官方】
  • 万字拆解 LLM 运行机制:Token、上下文与采样参数巡
  • Java开发中Lombok插件失效的常见问题与解决方案
  • 基于对比学习的无监督图片旋转判断方法