8.5. 获得大量缓冲
8.5. 获得大量缓冲
我们我们已经在前面章节中注意到的, 大量连续内存缓冲的分配是容易失败的. 系统内存长时间会碎片化, 并且常常出现一个真正的大内存区会完全不可得. 因为常常有办法不使用大缓冲来完成工作, 内核开发者没有优先考虑使大分配能工作. 在你试图获得一个大内存区之前, 你应当真正考虑一下其他的选择. 到目前止最好的进行大 I/O 操作的方法是通过发散/汇聚操作, 我们在第 1 章的"发散-汇聚 映射"一节中讨论了.
8.5.1. 在启动时获得专用的缓冲
如果你真的需要一个大的物理上连续的缓冲, 最好的方法是在启动时请求内存来分配它. 在启动时分配是获得连续内存页而避开 __get_free_pages 施加的对缓冲大小限制的唯一方法, 不但最大允许大小还有限制的大小选择. 在启动时分配内存是一个"脏"技术, 因为它绕开了所有的内存管理策略通过保留一个私有的内存池. 这个技术是不优雅和不灵活的, 但是它也是最不易失败的. 不必说, 一个模块无法在启动时分配内存; 只有直接连接到内核的驱动可以这样做.
启动时分配的一个明显问题是对通常的用户它不是一个灵活的选择, 因为这个机制只对连接到内核映象中的代码可用. 一个设备驱动使用这种分配方法可以被安装或者替换只能通过重新建立内核并且重启计算机.
当内核被启动, 它赢得对系统种所有可用物理内存的存取. 它接着初始化每个子系统通过调用子系统的初始化函数, 允许初始化代码通过减少留给正常系统操作使用的 RAM 数量, 来分配一个内存缓冲给自己用.
启动时内存分配通过调用下面一个函数进行:
#include <linux/bootmem.h>
void *alloc_bootmem(unsigned long size);
void *alloc_bootmem_low(unsigned long size);
void *alloc_bootmem_pages(unsigned long size);
void *alloc_bootmem_low_pages(unsigned long size);
这些函数分配或者整个页(如果它们以 _pages 结尾)或者非页对齐的内存区. 分配的内存可能是高端内存除非使用一个 _low 版本. 如果你在为一个设备驱动分配这个缓冲, 你可能想用它做 DMA 操作, 并且这对于高端内存不是一直可能的; 因此, 你可能想使用一个 _low 变体.
很少在启动时释放分配的内存; 你会几乎肯定不能之后取回它, 如果你需要它. 但是, 有一个接口释放这个内存:
void free_bootmem(unsigned long addr, unsigned long size);
注意以这个方式释放的部分页不返回给系统 -- 但是, 如果你在使用这个技术, 你已可能分配了不少数量的整页来用.
如果你必须使用启动时分配, 你需要直接连接你的驱动到内核. 应当如何完成的更多信息看在内核源码中 Documentation/kbuild 下的文件.