综述
本章主要学习设备驱动程序中,常用的分配内存的方法
kmalloc函数的内幕
1.函数原型
#include <linux/slab.h>
void *kmalloc(size_t size,ini flags);
参数一:分配内存的大小
参数二:分配标志
2.size参数
内核分配内存的方法:创建一系列的内存对象池,
每个池中的内存块大小是固定一致的。
内核只能分配一些预定义的、固定大小的字节数组。
kmalloc能处理的最小内存块是32或者64,取决于cpu平台。
3.参数二:flags标志
驱动开发最常用的2个标志位:
1.GFP_KERNEL
表示内存分配(最终总是调用get_free_pages来实现实际的分配,
这既是GFP_前缀的由来)是由运行在内核空间的进程执行的,
可能会引起休眠
2.GFP_ATOMIC
用于在进程上下文之外(如中断处理例程、tasklet以及内核定时器调用)的代码中分配内存,
不会休眠
------------------------------------------------------------------------
其他标志
1.GFP_USER
用于为用户空间分配内存,可能会休眠
2.GFP_HIGHUSER
类似于GFP_USER,如果存在高端内存的话,就从那里分配
3.GFP_NOIO
4.GFP_NOFS
这2个标志类似GFP_KERNEL,但是有一些限制,GFP_NOFS的分配不允许执行任何文件系统的调用。
而GFP_NOIO禁止任何I/O的初始化。
------------以上标志可以和下面标志“或”起来用------------
_ _GFP_DMA
请求分配发送在可进行DMA的内存区段中。
_ _GFP_HIGHMEM
表明要分配的内存可位于高端内存
_ _GFP_COLD
通常,内存分配会试图返回“缓存热”页面,即可在处理器缓存中找到的页面,
该标准请求尚未使用的"冷"页面。
_ _GFP_NOWARN
很少使用,避免在内核无法满足分配请求时产生警告
__GFP_HIGH
高优先级的请求,允许为紧急状态下而消耗由内存保存的最后一些页面
_ _GFP_REPEAT
内存分配失败时请求再次分配
_ _GFP_NOFAIT
不推荐使用,告诉分配器始终不返回失败
_ _GFP_NOREPEAT
如果内存请求失败,立即返回
4.内存区段
Linux内核把内存分为三个区段:
1.可用于DMA的内存
2.常规内存
3.高端内存(kmalloc不能分配高端内存)
后备高速缓存(lookaside cache)
1.slab分配器-用于创建高速缓存的对象
#include <linux/slab.h>
kmem_cache_t *kmem_cache_create(const char *name,size_t size,
size_t offset,
unsigned long flags,
void (*constructor)(void *,kmen_cache_t *,
unsigned long flags),
void (*destructor)(void *,kmem_cache_t*,
unsigned long flags));
参数:
const char *name:高速缓存对象的名称
size_t size:分配内存的大小
size_t offset:页面中第一个对象的偏移量,用于对齐,通常为0
unsigned long flags:分配标志
1.SLAB_NO_REAP 2.SLAB_HWCACHE_ALIGN 3.SLAB_CACHE_DMA
constructor和destructor参数可选的函数,
前者用于初始化新分配的对象,后者用于清除对象,一般都为NULL即可。
2.分配内存
void *kmem_cache_allpc(kmem_cache_t *cache,int flags);
参数一:cache是先前创建的高速缓存对象
参数二:flags和kmalloc的相同
3.释放内存对象
void kmem_cache_free(kmem_cache_t *cache,const void *obj);
4.释放内存
int kmem_cache_destory(kmem_cache_t *cache);
内存池
1.创建内存池的对象类型
#include <linux/mempool/h>
mempool_t *mempool_create(int min_nr
mempool_alloc_t *alloc_ fn
mempool_free_t *free_fn
void *pool_data);
cache = kmem_cache_create(...);
pool = mempool_create(8,mempool_alloc_slab,mempool_free_slab,cache);
2.分配和释放对象
void *mempool_alloc(mempool_t *pool,ini gfp_masl);
void mempool_free(void *element,mempool_t *pool);
3.调整mempool的大小
int mempool_resize(mempool_t *pool,init new_min_nr,int gfp_mask);
4.释放内存
void mempool_destroy(mempool_t *pool);
get_free_page
如果需要分配大块的内存,就应该使用面向页的分配技术。
1.分配内存
get_zeroed_page(unsigned int flags)
返回指向新页面的指针并且将页面清零
_ _get_free_page(unsigned int flags)
返回指向新页面的指针 但是页面不清零
_ _get_free_pages(unsigned int flags,unsigned int order)
分配若干、物理连续的页面,并返回指向该内存区域的第一个字节的指针,不清零页面
第一个参数和kmalloc一样,第二个参数是要申请或者释放的页面数,以2为底数(log2N)
2.释放内存
void free_page(unsigned long addr)
void free_pages(unsigned long addr,unsigned long order)
alloc_pages接口
1.struct page 用来描述单个内存页的数据结构
2.Linux页分配器的核心代码
struct page *alloc_pages_node(int nid,unsigned int flags,unsigned int order);
参数:
nid 是NUMA节点的ID号,表示要在其中分配内存。
flags和kmalloc的一样
order表示要分配的内存大小
变种
struct page *alloc_pages(unsigned int flags,unsigned int order);
struct page *alloc_page(unsigned int flags);
3.释放内存
void _ _free_page(struct page *p);
void _ _free_pages(struct page *p,unsigned int order);
void free_hot_page(struct page *p);
void free_cold_page(struct page *p);
vmalloc及辅助函数
1.vmaloc是内存分配函数,分配虚拟地址空间的连续区域(物理上可能不是连续的),它是linux内存分配机制的基础,但是不推荐使用此函数分配内存,只是了解即可。
#include <linux/vmalloc.h>
void * vmalloc(unsigned long size);
void * vfree(void *addr);
#include <asm/io.h>
void *ioremap(unsigned long offset,unsigned long size);
void iounmap(void * addr);
这些函数分配或者释放连续的虚拟地址空间。
ioremap通过虚拟地址访问物理内存,而vmalloc分配空闲页面。
使用ioremap映射的区域用iounmap释放。
per-CPU变量
#include <linux/percpu.h>
DEFINE_PER_CPU(type,name);
DECLARE_PER_CPU(type,name);
定义和声明per-CPU变量的宏
per-cpu(variable,int cpu_id);
get_cpu_var(variable);
put_cpu_var(vatiable);
用于访问静态声明的per-CPU的宏
void *alloc_percpu(type);
void * __alloc_percpu(size_t size,size_t align)
void free_percpu(void *variable);
执行per-CPU变量的运行时分配和释放的函数
int get_cpu();
void put_cpu();
per_cpu_ptr(void *variable,int cpu_id);
get_cpu获得对当前处理器的引用(避免抢占以及切换到其他处理器)并返回处理器的ID号;
而put_cpu返回该引用。为了访问动态分配的per-CPU变量,应使用per_cpi_ptr,
并且传递要访问的变量版本的CPU ID号。
对某个动态的per-CPU变量的当前CPU版本的操作,应该包含在对get_cpu和put_cpu的调用中间。
在系统引导时获得专用缓存区
#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);
void free_bootmem(unsigned long addr,unsigned long size);
在系统引导期间执行内核分配和 释放的函数。
这些函数只能在直接链接到内核的驱动程序中使用。
网友评论