一、kmalloc函数
原型:
#include <linux/slab.h>
void *kmalloc(size_t size, int flags);
最终总是调用get_free_pages(GFP)实现
1. flags参数
常用的标志:
GFP_KERNEL:运行于进程上下文,可休眠以等待一个页面(休眠时内核把缓冲区的内容刷写到硬盘,或者从一用户进程换出内存,以获取一个内存页面)
GFP_ATOMIC:原子性分配,可在进程上下文之外被调用(如中断处理进程、tasklet以及内核定时器)
还有一些标志控制如何进行分配,可以上面的“或”起来使用:
__GFP_DMA:分配DMA区段的内存
__GFP_HIGHMEM:可位于高端内存(但是kmalloc不能分配高端内存)
Linux内核把内存分为三个区段:DMA内存、常规内存、高端内存
2. size参数
内核只能分配一些预定义的、固定大小的字节数组
kmalloc能处理的最小的内存块是32或64,最大不该超过128KB(为了具有完整的移植性)
二、后备高速缓存
驱动程序经常反复分配同一大小的内存块,为这些块增加某些特殊的内存池,这种形式的内存池称为后备高速缓存(lookasidecache)
内核的高速缓存管理器有时称为“slab分配器”,它实现的高速缓存具有kmem_cache_t类型,通过kmem_cache_create创建
kmem_cache_t *kmem_cache_create(const char *name, size_t size,
size_t offset,
unsigned long flags,
void (*constructor)(void *, kmem_cache_t *,
unsigned long flags),
void (*destructor)(void *, kmem_cache_t *,
unsigned long flags));
该函数创建一个高速缓存对象,可容纳任意数目的内存区域,由size决定,name保管一些信息以便追踪(可直接用字符串),offset为第一个对象的偏移量
flags标志:
SLAB_NO_REAP:保护高速缓存不被减少(系统寻找内存时)
SLAB_HWCACHE_ALIGN:要求所有数据对象跟高速缓存行(cache line)对齐
constructor、destructor:可选参数(不能只有destructor而没有constructor),前者用于初始化新分配的对象,后者用于释放对象
void *kmem_cache_alloc(kmem_cache_t *cache, int flags);
/* 创建高速缓存对象后,可调用此函数从中分配内存对象,其中flags与kmalloc的相同 */
void kmem_cache_free(kmem_cache_t *cache, const void *obj);
/* 释放内存对象 */
int kmem_cache_destroy(kmem_cache_t *cache);
/* 释放高速缓存对象,需内存对象均归还才能成功,若失败->内存泄漏 */
可从/proc/slabinfo获得高速缓存的使用情况
三、get_free_page
分配大块内存,面向页,不产生内存碎片
get_zeroed_page(unsigned int flags);
/* 返回新页面的指针,并清零 */
__get_free_page(unsigned int flags);
/* 返回新页面的指针,不清零 */
__get_free_pages(unsigned int flags, unsigned int order);
/* 分配若干页(物理连续),返回第一个字节的指针,不清零 */
其中flags与kmalloc的一样,order是页面数的以2为底的对数(log 2 N),可以使用函数get_order()转换
void free_page(unsigned long addr);
void free_pages(unsigned long addr, unsigned long order);
/* 释放页面,第一个函数是一个宏,调用第二个函数 */
alloc_pages接口:
struct page *alloc_pages_node(int nid, unsigned int flags,unsigned int order);
/* Linux页分配器核心代码 */
struct page *alloc_pages(unsigned int flags, unsigned int order);
struct page *alloc_page(unsigned int flags);
/* 两个宏 */
void __free_page(struct page *page);
void __free_pages(struct page *page, unsigned int order);
void free_hot_page(struct page *page);//高速缓存中的
void free_cold_page(struct page *page);//高速缓存中的
/* 释放 */
四、vmalloc函数
1 .特性
虚拟地址连续,但物理地址可能不连续
分配的内存使用起来效率不高,不鼓励使用
与kmalloc的差异:kmalloc返回的虚拟地址与物理内存是一一对应的(可能有PAGE_OFFSET偏移),而vmalloc的虚拟地址需建立页表进行映射,故当需要真正的物理地址时不应使用vmalloc
不能用于原子上下文,内部调用kmalloc(GFP_KERNEL),可能休眠
2. 接口
#include <linux/vmalloc.h>
void *vmalloc(unsigned long size);//分配
void vfree(void * addr);//释放
/* 使用示例:create_module */
void *ioremap(unsigned long offset, unsigned long size);
void iounmap(void * addr);//释放
/*
与vmalloc一样会建立页表,但不实际分配内存
更多用于映射(物理的)PCI缓冲区地址到(虚拟的)内核空间
返回的地址需用readb或其他I/O函数来访问,不应直接访问
*/
五、获取大的、连续的缓冲区
方法:在引导时获得专用缓冲区
#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);
/* _low版本防止分配高端内存(高端内存不能用于DMA操作) */
void free_bootmem(unsigned long addr, unsigned long size);
/* 注意:释放的部分页面不会返回给系统 */