genalloc

本文深入探讨了内核模块中的genalloc内存分配机制,重点介绍了first-fit和best-fit两种算法。first-fit算法寻找第一个足够大的空闲内存块进行分配,而best-fit则选择最合适的空闲块,尽管后者分配速度较慢,但能有效减少内存碎片。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载自  https://www.douban.com/group/topic/47566535/?refer=related

 

内核的genalloc是给一些需要自己维护内存分配的内核模块使用的,如SRAM, TCM, Shared-memory等。它提供了最先适配 fist-fit和最佳匹配 best-fit两种分配算法。 

first-fit 和 best-fit 
这两种都是通过Bitmap来标记内存中每个分配单元的状态,空闲或已分配。 最先分配是将第一个可容纳请求大小的的连续空闲空间分配出去。 最佳分配是将可容纳请求大小的的最小连续空闲空间分配出去。 假设某片内存被划分为10个最小单 元,这两种分配方法演示如下: 

Operation First-fit Best-fit 
Initial state ➀➁➂➃➄➅➆➇➈➉ ➀➁➂➃➄➅➆➇➈➉ 
A: alloc(4) ➊➋➌➍➄➅➆➇➈➉ ➊➋➌➍➄➅➆➇➈➉ 
B: alloc(1) ➊➋➌➍➎➅➆➇➈➉ ➊➋➌➍➎➅➆➇➈➉ 
C: alloc(2) ➊➋➌➍➎➏➐➇➈➉ ➊➋➌➍➎➏➐➇➈➉ 
D: free(A) ➀➁➂➃➎➏➐➇➈➉ ➀➁➂➃➎➏➐➇➈➉ 
E: alloc(2) ➊➋➂➃➎➏➐➇➈➉ ➀➁➂➃➎➏➐➑➒➉ 
F: alloc(4) failed ➊➋➌➍➎➏➐➑➒➉ 
可以看出,这两种方法都会容易产生内存碎片,但Best-fit比First-fit要好一些,当然 这是以牺牲分配速度为代价的。 

<think>我们正在处理一个关于修改Linux内核以创建特定设备文件(/dev/dma_heap/reserved-uncached)的问题。首先,我们需要理解DMA-BUF堆子系统,因为/dev/dma_heap/*设备文件通常与DMA-BUF堆有关。 步骤: 1. 了解DMA-BUF堆:DMA-BUF堆是用于分配内存缓冲区(DMA缓冲区)的框架,这些缓冲区可以在不同设备或驱动之间共享。每个堆由一个设备文件表示(如/dev/dma_heap/system)。 2. 目标:创建一个名为“reserved-uncached”的堆,该堆使用保留内存(reserved memory)并且是非缓存的(uncached)。 3. 修改内核代码: a. 添加一个新的堆实现(通常在内核源码的drivers/dma-buf/heaps/目录下)。 b. 配置该堆使用保留内存区域(可能通过设备树或内核命令行预留内存)。 c. 设置该堆分配的内存为非缓存。 4. 实现步骤: - 在内核的drivers/dma-buf/heaps/目录下创建一个新的堆驱动,例如reserved_uncached_heap.c。 - 实现堆的创建、分配和释放等操作。 - 在堆的初始化函数中,将堆注册到DMA-BUF堆框架中。 5. 保留内存:通常,保留内存可以通过设备树(Device Tree)或内核命令行(如memmap=)预留。然后在内核驱动中获取这块内存区域。 6. 非缓存属性:在分配内存时,使用dma_alloc_attrs并设置DMA_ATTR_NO_KERNEL_MAPPING和DMA_ATTR_NON_CONSISTENT等属性,或者使用ioremap_nocache来映射物理内存。 7. 编译并加载该驱动。 详细步骤: 1. 创建堆驱动文件: 在drivers/dma-buf/heaps/目录下创建reserved_uncached_heap.c。 2. 实现堆操作: - 定义堆的结构体,包含dma_heap结构。 - 实现分配函数(allocate),在该函数中分配保留内存并设置非缓存属性。 3. 获取保留内存: 使用of_reserved_mem_device_init()(如果通过设备树预留)或者使用memblock分配器获取预留内存。 4. 非缓存内存分配: 使用dma_alloc_attrs()函数,并设置DMA_ATTR_NON_CONSISTENT和DMA_ATTR_NO_KERNEL_MAPPING,或者使用dma_alloc_from_contiguous()和dma_alloc_writecombine()(具体取决于架构和需求)。 5. 注册堆: 在模块初始化函数中,调用dma_heap_add()注册堆。 6. 修改Kconfig和Makefile: 在drivers/dma-buf/heaps/Kconfig中添加新堆的配置选项,在Makefile中添加编译条目。 7. 配置设备树(可选): 如果需要通过设备树指定保留内存区域,则添加相应的节点。 8. 编译内核模块或整个内核。 9. 测试:加载模块后,检查/dev/dma_heap/reserved-uncached是否出现,并使用测试程序分配内存。 注意:由于内核版本不同,具体实现细节可能有所变化。以下是一个基于Linux 5.10及以上版本的示例代码框架。 示例代码(reserved_uncached_heap.c): ```c #include <linux/dma-heap.h> #include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/genalloc.h> #include <linux/io.h> #include <linux/mm.h> #include <linux/module.h> #include <linux/of_reserved_mem.h> #include <linux/platform_device.h> #include <linux/slab.h> struct reserved_uncached_heap { struct gen_pool *pool; phys_addr_t base; size_t size; }; static struct dma_heap *heap; static int reserved_uncached_heap_allocate(struct dma_heap *heap, unsigned long len, unsigned long fd_flags, unsigned long heap_flags) { // 实现分配函数,这里需要分配保留内存并设置为非缓存 // 使用gen_pool_alloc从预留内存池中分配内存 // 然后使用dma_map_single_attrs设置非缓存属性 // 最后将内存导出为DMA-BUF // 由于代码较长,这里只给出框架 return -ENOTSUPP; // 实际实现需要完成 } static const struct dma_heap_ops reserved_uncached_heap_ops = { .allocate = reserved_uncached_heap_allocate, }; static int rmem_reserved_uncached_device_init(struct reserved_mem *rmem, struct device *dev) { // 初始化设备,将保留内存关联到设备 struct reserved_uncached_heap *res_heap; int ret; res_heap = devm_kzalloc(dev, sizeof(*res_heap), GFP_KERNEL); if (!res_heap) return -ENOMEM; res_heap->base = rmem->base; res_heap->size = rmem->size; // 创建gen_pool,用于管理预留内存 res_heap->pool = gen_pool_create(PAGE_SHIFT, -1); if (!res_heap->pool) { dev_err(dev, "Failed to create gen pool\n"); return -ENOMEM; } ret = gen_pool_add_virt(res_heap->pool, (unsigned long)phys_to_virt(res_heap->base), res_heap->base, res_heap->size, -1); if (ret) { dev_err(dev, "Failed to add memory to pool\n"); gen_pool_destroy(res_heap->pool); return ret; } dev_set_drvdata(dev, res_heap); return 0; } static void rmem_reserved_uncached_device_release(struct reserved_mem *rmem, struct device *dev) { // 清理 } static const struct reserved_mem_ops rmem_ops = { .device_init = rmem_reserved_uncached_device_init, .device_release = rmem_reserved_uncached_device_release, }; static int __init reserved_uncached_heap_init(void) { struct dma_heap_export_info exp_info = { .name = "reserved-uncached", .ops = &reserved_uncached_heap_ops, .priv = NULL, // 这里可以传递私有数据 }; heap = dma_heap_add(&exp_info); if (IS_ERR(heap)) return PTR_ERR(heap); return 0; } module_init(reserved_uncached_heap_init); static void __exit reserved_uncached_heap_exit(void) { dma_heap_put(heap); } module_exit(reserved_uncached_heap_exit); MODULE_LICENSE("GPL"); ``` 注意:以上代码仅为框架,实际分配函数需要完善,包括分配内存、创建DMA-BUF、设置非缓存属性等。 设备树配置示例: ``` reserved-memory { #address-cells = <2>; #size-cells = <2>; ranges; reserved_uncached_region: reserved-uncached@80000000 { compatible = "reserved-memory-region"; reg = <0x0 0x80000000 0x0 0x10000000>; // 256MB no-map; }; }; reserved_uncached_heap: reserved-uncached-heap { compatible = "reserved-memory-region"; memory-region = <&reserved_uncached_region>; }; ``` 然后,在驱动中需要解析设备树节点,获取内存区域。 由于实现较为复杂,且需要根据具体硬件和内核版本调整,建议参考内核中现有的堆实现(如system_heap、cma_heap等)。 相关问题: 1. 如何在内核中预留内存区域? 2. 如何在内核中分配非缓存内存? 3. DMA-BUF堆框架的基本结构是什么? 4. 如何在内核模块中创建设备文件? 5. 如何测试新创建的DMA-BUF堆?</think>要修改 Linux 内核以创建 `/dev/dma_heap/reserved-uncached` 设备文件,需要在内核 DMA-BUF 堆子系统中添加新的堆实现。以下是详细步骤: --- ### 一、添加新的堆实现 1. **创建堆驱动文件** 在 `drivers/dma-buf/heaps/` 目录下新建 `reserved_uncached_heap.c`: ```c #include <linux/dma-heap.h> #include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/genalloc.h> #include <linux/module.h> #include <linux/of_reserved_mem.h> #include <linux/platform_device.h> #include <linux/slab.h> struct reserved_uncached_heap { struct gen_pool *pool; phys_addr_t base; size_t size; }; static int reserved_uncached_allocate(struct dma_heap *heap, unsigned long len, unsigned long fd_flags, unsigned long heap_flags) { struct reserved_uncached_heap *ru_heap = dma_heap_get_drvdata(heap); unsigned long offset = gen_pool_alloc(ru_heap->pool, len); if (!offset) return -ENOMEM; // 创建 DMA-BUF(此处简化,实际需实现完整 dma_buf 操作) return dma_heap_add_buf(heap, ru_heap->base + offset, len); } static const struct dma_heap_ops reserved_uncached_ops = { .allocate = reserved_uncached_allocate, }; static int reserved_uncached_heap_probe(struct platform_device *pdev) { struct reserved_uncached_heap *ru_heap; struct dma_heap_export_info exp_info = { .name = "reserved-uncached", .ops = &reserved_uncached_ops, .priv = NULL }; ru_heap = devm_kzalloc(&pdev->dev, sizeof(*ru_heap), GFP_KERNEL); if (!ru_heap) return -ENOMEM; // 从设备树获取预留内存区域 if (of_reserved_mem_device_init(&pdev->dev)) return -ENODEV; ru_heap->base = devm_memremap(&pdev->dev, ...); // 映射物理地址 ru_heap->size = ...; // 获取大小 ru_heap->pool = gen_pool_create(PAGE_SHIFT, -1); gen_pool_add_virt(ru_heap->pool, (unsigned long)ru_heap->base, ru_heap->base, ru_heap->size, -1); exp_info.priv = ru_heap; dma_heap_add(&exp_info); return 0; } static const struct of_device_id reserved_uncached_of_match[] = { { .compatible = "reserved-uncached-heap" }, {} }; MODULE_DEVICE_TABLE(of, reserved_uncached_of_match); static struct platform_driver reserved_uncached_driver = { .driver = { .name = "reserved_uncached_heap", .of_match_table = reserved_uncached_of_match, }, .probe = reserved_uncached_heap_probe, }; module_platform_driver(reserved_uncached_driver); ``` 2. **设置非缓存属性** 在分配函数中添加内存属性: ```c static int reserved_uncached_allocate(...) { // ... pgprot_val(pgprot) &= ~_PAGE_CACHE_MASK; pgprot_val(pgprot) |= _PAGE_UNCACHED; // 架构相关(如 x86) // 或使用 set_memory_uc() 设置非缓存 } ``` --- ### 二、配置内核编译 1. **修改 Makefile** 在 `drivers/dma-buf/heaps/Makefile` 添加: ```makefile obj-$(CONFIG_DMABUF_HEAPS_RESERVED_UNCACHED) += reserved_uncached_heap.o ``` 2. **添加 Kconfig 选项** 在 `drivers/dma-buf/heaps/Kconfig` 添加: ```kconfig config DMABUF_HEAPS_RESERVED_UNCACHED tristate "Reserved Uncached DMA-BUF Heap" depends on DMABUF_HEAPS help Heap for reserved uncached memory allocations. ``` 3. **编译内核** ```bash make menuconfig # 启用 DMABUF_HEAPS_RESERVED_UNCACHED make -j$(nproc) ``` --- ### 三、设备树配置 在设备树中声明预留内存区域: ```dts reserved-memory { #address-cells = <2>; #size-cells = <2>; ranges; reserved_uncached: reserved-uncached@80000000 { compatible = "reserved-memory-region"; reg = <0x0 0x80000000 0x0 0x10000000>; // 256MB no-map; }; }; reserved_uncached_heap: reserved-uncached-heap { compatible = "reserved-uncached-heap"; memory-region = <&reserved_uncached>; }; ``` --- ### 四、验证设备文件 1. 启动新内核后,检查设备文件: ```bash ls -l /dev/dma_heap/reserved-uncached # 输出应显示字符设备 ``` 2. 使用测试工具分配内存: ```bash dmabuf-heap-test -d reserved-uncached -s 4096 ``` --- ### 关键注意事项 1. **内存管理**:需确保预留内存不被内核其他部分占用(通过 `reserved-memory` 节点)。 2. **缓存一致性**:非缓存内存需手动处理同步(如 `dma_sync_single_for_device()`)[^1]。 3. **架构适配**:非缓存属性设置(如 `_PAGE_UNCACHED`)需根据 CPU 架构调整。 4. **DMA-BUF 框架**:完整实现需补充 `dma_buf_ops` 操作集(如 `mmap`, `release`)。 [^1]: 参考内核文档 `Documentation/core-api/dma-api.rst` 和 `Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt`。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值