LDD读书笔记第八章-分配内存
kmalloc函数的内幕
kmalloc函数在没有阻塞的情况下运行速度很快。
并且不会对申请到的内存区域清空,所以在使用时必须清空。
函数原型
1 2 | #include <linux/slab.h> void *kmalloc(size_t size, int flags); |
第一个参数size 表明要申请的内存区域大小。
flags是分配标志,能以多种方法控制kmalloc的行为。
flags参数
带”__“的标志可与不带的版本一起“或”来使用。
GFP_ATOMIC
用于在中断处理列程或运行在非进程上下文的代码(也就是不能休眠的代码)中使用,不会休眠。
GFP_KERNEL
内核一般的分配内存的方法,可能会休眠
GFP_USER
为用户空间分配内存时使用,可能会休眠
GFP_HIGHUSER
和GFP_USER相似,但是会分配高端内存。
GFP_NOIO
GFP_NOFS
类似于GFP_KERNEL但是有少许限制,GFP_NOFS禁止执行任何文件系统调用,GFP_NOIO禁止任何IO初始化。
__GFP_DMA
请求分配发生在可进行DMA的内存区段中。
__GFP_HIGHMEM
可位于高端内存
__GFP_COLD
会申请“冷”页面。
DMA buffers.
__GFP_NOWARN
避免内核在无法分配时产生警告。
__GFP_HIGH
高优先级申请,可消耗,内核为紧急情况保留的最后一点页面。
__GFP_REPEAT
申请失败时再试一次。
__GFP_NOFAIL
申请失败时始终不返回失败,努力满足分配需求。
__GFP_NORETRY
如果不可获得就立即返回
内存区段
内存的三个区段:可用于DMA的内存,常规内存,高端内存。
通常的内存申请发生在常规内存里
高端内存是为了在32位平台上访问大量的内存而用的一种机制。
需要进行映射,比较难处理
DMA区段为可用于DMA的特别地址范围的内存,外设可以使用这些内存执行DMA。
Direct Memory Access(存储器直接访问)。这是指一种高速的数据传输操作,允许在外部设备和存储器之间直接读写数据,既不通过CPU,也不需要CPU干预。整个数据传输操作在一个称为”DMA控制器”的控制下进行的。CPU除了在数据传输开始和结束时做一点处理外,在传输过程中CPU可以进行其他的工作。这样,在大部分时间里,CPU和输入输出都处于并行操作。因此,使整个计算机系统的效率大大提高。
后备高速缓存
如果需要反复申请相同大小的内存区域。可使用后备高速缓存。
后备高数缓存有时称为“slab分配器”
1 2 3 4 5 6 7 8 9 | #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 *, 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
所有数据对象跟高速缓存行对其,在SMP中如果经常访问某个数据的话可提高性能,但是空白填充会浪费内存。
SLAB_CACHE_DMA
数据对象都从可用于DMA的内存区段中匹配。
constructor和destructor是可选项。前者用于初始化后者用于清除。如果不指定应写NULL。
建立完成后可申请和释放内存。
1 2 3 4 5 | /*申请与释放*/ void *kmem_cache_alloc(kmem_cache_t *cache, int flags); void kmem_cache_free(kmem_cache_t *cache, const void *obj); /*释放高速缓存,可能失败,失败时可能造成内存泄漏*/ int kmem_cache_destroy(kmem_cache_t *cache); |
内存池
在某些不允许申请内存失败的情况下使用内存池。
内存池是某种形式的后备高速缓存,它试图始终保持空闲的内存,以便在紧急情况下使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn, mempool_free_t *free_fn, void *pool_data); /*nr是保持的最小数目*/ /*alloc_fn和free_fn分别是负责分配和释放的函数,原型为*/ typedef void *(mempool_alloc_t)(int gfp_mask, void *pool_data); typedef void (mempool_free_t)(void *element, void *pool_data); /*通常让slab分配器帮我们处理这些任务,使用mempool_alloc_slab 和 mempool_free_slab 示列代码为:*/ cache = kmem_cache_create(. . .); pool = mempool_create(MY_POOL_MINIMUM, mempool_alloc_slab, mempool_free_slab,cache); /*用如下代码申请或释放内存*/ void *mempool_alloc(mempool_t *pool, int gfp_mask); void mempool_free(void *element, mempool_t *pool); /*调整内存池大小*/ int mempool_resize(mempool_t *pool, int new_min_nr, int gfp_mask); /*释放内存池*/ void mempool_destroy(mempool_t *pool); |
get_free_page和相关函数
需要大量内存的时候使用页面来分配。
分配函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | get_zeroed_page(unsigned int flags); /*申请一个页面并清零*/ __get_free_page(unsigned int flags); /*申请一个页面但不清零*/ __get_free_pages(unsigned int flags, unsigned int order); /*申请连续的几个页面,不清零*/ /*flag参数和kmalloc一样,order是需要申请的页面的数量以2为底数的对数*/ void free_page(unsigned long addr); void free_pages(unsigned long addr, unsigned long order); /*第一个是个宏展开成第二个函数*/ <h4>alloc_pages接口</h4> struct page是内核用来描述单个页面的数据结构。 <pre lang="C">/*申请*/ struct page *alloc_pages_node(int nid, unsigned int flags, unsigned int order); 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及其补助函数
vmalloc提供连续的虚拟内存空间,这些内存空间在物理上不是连续的。使用时会通过MMU处理转换成物理内存地址。
相关函数,vmalloc一般使用在软件中需要大量缓冲区的情况。
1 2 3 4 | void *vmalloc(unsigned long size); void vfree(void * addr); void *ioremap(unsigned long offset, unsigned long size); void iounmap(void * addr); |
kmalloc和get_free_pages返回的地址也是虚拟的。但是地址范围和物理地址是一一对应的。
可能基于某个偏移地址来实现。所以不需要为地址修改也表,但是vmalloc在物理地址中是不连续的。
需要通过页表来访问物理地址,vmalloc可以获得的地址在VMALLOC_START和VMALLOC_END之间,这两个符号在asm/pgtable.h中。
vmalloc的地址只能使用在处理器中,不能使用在如dma的场合。
ioremap用于映射指定的内存区域。
per-CPU变量
当建立一个per-CPU变量时,系统中每个处理器都用该变量的特定副本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #include <linux/percpu.h> /*建立percpu变量*/ DEFINE_PER_CPU(type, name); void *alloc_percpu(type); void *__alloc_percpu(size_t size, size_t align); /*获取和更新数据*/ get_cpu_var(sockets_in_use); put_cpu_var(sockets_in_use); per_cpu(variable, int cpu_id); per_cpu_ptr(void *per_cpu_var, int cpu_id); /*使用动态非配的变量时使用get_cpu和put_cpu来阻止抢占*/ int cpu; cpu = get_cpu( ) ptr = per_cpu_ptr(per_cpu_var, cpu); /* work with ptr */ put_cpu( ); /*percpu变量可以用下列宏来导出*/ EXPORT_PER_CPU_SYMBOL(per_cpu_var); EXPORT_PER_CPU_SYMBOL_GPL(per_cpu_var); /*访问*/ DECLARE_PER_CPU(type, name); |
获取大的缓冲区
大的内存区通常不容易获取到,通常可以使用离散/聚集操作来实现。
在引导时获得专用缓冲区
只有直接链接到内核的代码才能引导时获取缓冲区。
分配和释放使用下列函数
1 2 3 4 5 6 7 | #include <linux/bootmen.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); |
分配要么是页(pages版本)要么是页边界对其的空间,除非使用_low后缀的版本否者可能在高端内存。
© 除非另有注明本文采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可。
© 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
© 转载请注明: LDD读书笔记第八章-分配内存 | 转自: Senghoo's Blog
© Tag: 内存分配 , 内核