常见的 Linux内核中内存分配函数


常见的 Linux 内核中内存分配函数 1.原理说明 Linux 内核中采 用了一种同时适用于 32 位和 64 位系统的内 存分页模型,对于 32 位系 统来说,两级页表足够用了,而在 x86_64 系 统中,用到了四级页表,如图 2-1 所示。四级 页表分别为: * 页全局目录(Page Global Directory) * 页上级目录(Page Upper Directory) * 页中间目录(Page Middle Directory) * 页表(Page Table) 页全局目录包含若干页上级目录的地址,页上级目录又依次包含若干页中间目录的地址, 而页中间目录又包含若干页表的地址,每一个页表项指 向一个页框。Linux 中采用 4KB 大小 的 页框作为标准的内存分配单元。 多级分页目录结构 1.1.伙伴系统算法 在实际应用中,经常需要分配一组连续的页框,而频繁地申请和释放不同大小的连续页 框,必然导致在已分配页框的内存块中分散了许多小块的 空闲页框。这样,即使这些页框 是空闲的,其他需要分配连续页框的应用也很难得到满足。 为了避免出现这种情况,Linux 内核中引入了伙伴系统算法(buddy system)。把所有的空 闲页框分组为 11 个 块链表,每个块链表分别包含大小为 1,2,4,8,16,32,64,128, 256,512 和 1024 个连续页框的页框块。最大可以申请 1024 个连 续页框,对应 4MB 大小 的连续内存。每个页框块的第一个页框的物理地址是该块大小的整数倍。 假设要申请一个 256 个页框的块,先从 256 个页框的链表中查找空闲块,如果没有,就 去 512 个 页框的链表中找,找到了则将页框块分为 2 个 256 个 页框的块,一个分配给应用, 另外一个移到 256 个页框的链表中。如果 512 个页框的链表中仍没有空闲块,继续向 1024 个页 框的链表查找,如果仍然没有,则返回错误。 页框块在释放时,会主动将两个连续的页框块合并为一个较大的页框块。 1.2.slab 分 配器 slab 分配器源于 Solaris 2.4 的 分配算法,工作于物理内存页框分配器之上,管理特定 大小对象的缓存,进行快速而高效的内存分配。 slab 分配器为每种使用的内核对象建立单独的缓冲区。Linux 内核已经采用了伙伴系统 管理物理内存页框,因此 slab 分配器直接工作于伙伴系 统之上。每种缓冲区由多个 slab 组 成,每个 slab 就是一组连续的物理内存页框,被划分成了固定数目的对象。根据对象大小 的不同,缺省情况下一个 slab 最多可以由 1024 个页框构成。出于对齐 等其它方面的要求, slab 中分配给对象的内存可能大于用户要求的对象实际大小,这会造成一定的 内存浪费。 2.常用内存分配函数 2.1.__get_free_pages unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order) __get_free_pages 函数是最原始的内存分配方式,直接从伙伴系统中获取原始页框,返 回值为第一个页框的起始地址。__get_free_pages 在实现上只是封装了 alloc_pages 函 数, 从代码分析,alloc_pages 函数会分配长度为 1。 2.2.kmem_cache_alloc struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align, unsigned long flags, void (*ctor)(void*, struct kmem_cache *, unsigned long), void (*dtor)(void*, struct kmem_cache *, unsigned long)) void *kmem_cache_alloc(struct kmem_cache *c, gfp_t flags) kmem_cache_create/ kmem_cache_alloc 是基于 slab 分配器的一种内存分配方式,适用 于反复分配释放同一大小内存块的场合。首先用 kmem_cache_create 创建一个高速缓存区域, 然后用 kmem_cache_alloc 从 该高速缓存区域中获取新的内存块。 kmem_cache_alloc 一次 能分配的最大内存由 mm/slab.c 文件中的 MAX_OBJ_ORDER 宏 定义,在默认的 2.6.18 内核 版本中,该宏定义为 5, 于是一次最多能申请 1<<5 * 4KB 也就是 128KB 的 连续物理内存。 分析内核源码发现,kmem_cache_create 函数的 size 参数大于 128KB 时会调用 BUG()。测试 结果验证了分析结果,用 kmem_cache_create 分 配超过 128KB 的内存时使内核崩溃。 2.3.kmalloc void *kmalloc(size_t size, gfp_t flags) kmalloc 是内核中最常用的一种内存分配方式,它通过调用 kmem_cache_alloc 函 数来 实现。kmalloc 一次最多能申请的内存大小由 include/Linux/Kmalloc_size.h 的 内容来决定, 在默认的 2.6.18 内核版本中,kmalloc 一 次最多能申请大小为 131702B 也就是 128KB 字 节 的连续物理内存。测试结果表明,如果试图用 kmalloc 函数分配大于 128KB 的内存,编译不 能通过。 2.4.vmalloc void *vmalloc(unsigned long size) 前面几种内存分配方式都是物理连续的,能保证较低的平均访问时间。但是在某些场合 中,对内存区的请求不是很频繁,较高的内存访问时间也 可以接受,这是就可以分配一段 线性连续,物理不连续的地址,带来的好处是一次可以分配较大块的内存。图 3-1 表 示的 是 vmalloc 分配的内存使用的地址范围。vmalloc 对 一次能分配的内存大小没有明确限制。 出于性能考虑,应谨慎使用 vmalloc 函数。在测试过程中, 最大能一次分配 1GB 的空间。 Linux 内核部分内存分布 2.5.dma_alloc_coherent void *dma_alloc_coherent(struct device *dev, size_t size, ma_addr_t *dma_handle, gfp_t gfp) DMA 是一种硬件机制,允许外围设备和主存之间直接传输 IO 数据,而不需要 CPU 的参 与,使用 DMA 机制能大幅提高与设备通信的 吞吐量。DMA 操作中,涉及到 CPU 高速缓 存 和对应的内存数据一致性的问题,必须保证两者的数据一致,在 x86_64 体系结构中,硬件 已经很 好的解决了这个问题, dma_alloc_coherent 和__get_free_pages 函数实现差别不大, 前者实际是调用__alloc_pages 函 数来分配内存,因此一次分配内存的大小限制和后者一样。 __get_free_pages 分配的内 存同样可以用于 DMA 操作。测试结果证明,dma_alloc_coherent 函 数一次能分配的最大内存也为 4M。 2.6.ioremap void * ioremap (unsigned long offset, unsigned long size) ioremap 是一种更直接的内存“分配”方式,使用时直接指定物理起始地址和需要分配 内存的大小,然后将该段 物理地址映射到内核地址空间。ioremap 用到的物理地址空间都 是事先确定的,和上面的几种内存 分配方式并不太一样,并不是分配一段新的物理内存。 ioremap 多用于设备驱动,可以让 CPU 直接访问外部设备的 IO 空间。ioremap 能映射的内存 由原有的物理内存空间决定,所以没有进行测试。 2.7.Boot Memory 如果要分配大量的连续物理内存,上述的分配函数都不能满足,就只能用比较特殊的方 式,在 Linux 内 核引导阶段来预留部分内存。 2.7.1.在内核引导时分配内存 void* alloc_bootmem(unsigned long size) 可以在 Linux 内核引导过程中绕过伙伴系统来分配大块内存。使用方法是在 Linux 内核 引导时,调用 mem_init 函数之前 用 alloc_bootmem 函数申请指定大小的内存。如果需要在 其他地方调用这块内存,可以将 alloc_bootmem 返回的内存首地址通过 EXPORT_SYMBOL 导 出,然后就可以使用这块内存了。这种内存分配方式的缺点是,申请内存的代码必须在链接 到内核中的代码里才能使用,因此必须重新编译内核,而且内存管理系统 看不到这部分内 存,需要用户自行管理。测试结果表明,重新编译内核后重启,能够访问引导时分配的内存 块。 2.7.2.通过内核引导参数预留顶部内存 在 Linux 内核引导时,传入参数“mem=size”保留顶部的内存区间。比如系统有 256MB 内 存, 参数 “ mem=248M”会预留顶部的 8MB 内存,进入系统后可以调用 ioremap(0xF800000, 0x800000)来申请这段内存。 3.几种分配函数的比较 分配原理最大内存其他 __get_free_pages 直接对页框进行操作 4MB 适用于分配较大量的连续物理内存 kmem_cache_alloc 基于 slab 机制实现 128KB 适合需要频繁申请释放相同大小内存块时 用 kmalloc 基于 kmem_cache_alloc 实现 128KB 最常见的分配方式,需要小于页框大小的内 存时可以使用 vmalloc 建立非连续物理内存到虚拟地址的映射物理不连续,适合需要大内存,但是对 地址连续性没有要求的场合 dma_alloc_coherent 基于__alloc_pages 实现 4MB 适用于 DMA 操 作 ioremap 实现已知物理地址到虚拟地址的映射适用于物理地址已知的场合,如设备驱动 alloc_bootmem 在启动 kernel 时,预留一段内存,内核看不见小于物理内存大小,内存 管理要求较高
还剩2页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 3 金币 [ 分享pdf获得金币 ] 0 人已下载

下载pdf

pdf贡献者

monj78h

贡献于2014-02-25

下载需要 3 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf