Linux设备驱动程序学习笔记——第八章分配内存教程
Linux设备驱动程序学习笔记
第八章分配内存
一、kmalloc函数的内幕
(1)flags参数
//kmalloc原型
#include<linux/slab.h>
void *kmalloc(size_t size, int flags);//flags分配标志,最常用的是GFP_FERNEL
分配标志:
GFP\_ATOMIC用于在中断处理例程或其他运行于进程上下文以外的代码中分配内存如tasklet以及内核定时器调用,不会休眠GFP\_KERNEL内核内存的通常分配方法,可能引起休眠GFP\_USER用于为用户空间页分配内存,可能会休眠GFP\_HIGHUSER类似于GFP\_USER,不过如果有高端内存的话就从这里分配GFP\_NOIO、GFP\_NOFS类似GFP\_KERNRL,但是为内核分配内存的方式添加了一些限制,具有GFP\_NOFS标志的分配不允许执行任何文件系统调用,而GFP\_NOIO禁止任何I/O初始化
(2)内存区段
Linux吧内存氛围三个区段,可用于DMA的内存,常规内存以及高端内存
(3)size参数
kmalloc能处理的最小内存块是32或者64,取决于体系结构使用的页面大小。
如果要保证完整的可移植性,则不应该分配大于128KB的内存。
二、后备告诉缓存
后备高速缓存管理有时称为slab分配器,相关函数和类型在<linux/slab.h>中声明。
slab分配器实现的高速缓存具有kmem\_cache\_t类型,可通过调用kmem\_cache\_creat创建:
kmem\_cache *kmem\_cache\_create(const char *name, size\_t size, size\_t align,
unsigned long flags, void (*ctor)(void *))
- name字符串,存放高速缓存的名字
- size高速缓存中每个元素的大小
- align即slab内第一个对象的偏移量,用来保证在页内进行特定的对齐,通常为0
- flag为可选设置项,用来控制高速缓存行为
- ctor是高速缓存的构造函数,只有在新的页追加到高速缓存时,构造函数才被调用,但事实上并不适用,可以赋值NULL
- eg:task\_struct\_cachep =
kmem\_cache\_create(“task\_struct”, arch\_task\_struct\_size,
ARCH\_MIN\_TASKALIGN, SLAB\_PANIC | SLAB\_NOTRACK, NULL);
释放高速缓存:
void kmem\_cache\_destroy(struct kmem\_cache *);
分配对象:
void *kmem\_cache\_alloc(struct kmem\_cache *cachep, gfp\_t flags)
释放内存对象:
void kmem\_cache\_free(struct kmem\_cache *cachep, void *objp)
(1)基于slab高速缓存scull:scullc
略
(2)内存池
内存池其实就是某种形式的后备高速缓存,试图始终保存空闲的内存,以便在紧急状态下使用。
内存池对象的类型为mempool\_t,定义在<linux/mempool.h>中
创建:
/*min_nr表示内存池应始终保持的已分配对象的最少数目*/
mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn,
mempool_free_t *free_fn, void *pool_data)
后略。mempool会分配一些内存块,空闲且不会真正得到使用,因为很容易浪费大量内存。应避免使用mempool
三、get\_free\_page和相关函数
如果需要大块的内存,面向页的分配技术会更好些。
get\_zeroed\_page(unsigned int flags)//返回指向新页面的指针,并将页面清零
\_\_get\_free\_page(unsigned int flags)//类似上述函数,但不清零。
\_\_get\_free\_pages(unsigned int flags, unsigned int order)//分配2^order个页面。
void free\_page(unsigned long addr);
void free\_pages(unsigned long addr, unsigned long order);
要注意分配失败时提供相应的处理。
相比于kmalloc,基于页的分配策略速度有一定提高,并且能更有效的实用内存,不会浪费内存空间。并且这些页面完全属于我们,可以通过适当的调整页表将他们合并成一个线性区域。
alloc\_pages
十五章才会使用,这里只做介绍。
与get\_free\_page类似,但其返回page指针
static inline struct page *alloc_pages_node(int nid, gfp_t gfp_mask,//nid是NUMA节点的ID号
unsigned int order)
static inline struct page *alloc_pages(gfp_t gfp_mask, unsigned int order)//默认当前nid;
static inline struct page *alloc_page(gfp_t gfp_mask)
void __free_pages(struct page *page, unsigned int order)
void __free_page(struct page *page)
* Free a 0-order page
* cold == true ? free a cold page 不在cache中 : free a hot page 在cache中
*/
void free_hot_cold_page(struct page *page, bool cold)
/*如果提前知道页面中的内容是否驻留在处理器高速缓存中,应只用free hot或free cold,*/
/*可帮助内存分配其优化内存使用*/
四、vmalloc及其辅助函数
分配虚拟地址空间的连续区域,尽管其在物理上可能不连续,但内核认为他在地址上是连续的。当驱动程序需要真正的物理地址时,不能使用vmalloc。
使用vmalloc函数的正确场合是在分配一大块连续的,只在软件中存在的,用于缓冲的内存区域的时候。
注意vmalloc的开销要比\_\_get\_free\_pages要大,因为不但获取内存,还要建立页表。因此用vmalloc函数分配仅一页的内存空间是不值得的。
五、pre-CPU变量
当建立一个per-CPU变脸时,系统中的每个处理器都会拥有该变量的特有副本。优点是访问不需要锁定,因为每个处理器在其自己的副本上工作,还可以保存在对应处理器的cache中。
使用实例如网络子系统,内和维护大量计数器,当需要时统计各个处理器的per-CPU计数器即可。
六、获取大的缓冲区
(1)在引导时获得专用缓冲区
如果的确需要连续的大块内存需要缓冲区,最好在系统引导期间通过请求内存来分配。