Linux内核学习:kmalloc
来源:互联网 发布:免费淘宝客推广软件 编辑:程序博客网 时间:2024/06/05 20:11
kmalloc的内核源码分析
kmalloc在内核中的定义如下:(include/linux/slab_def.h)
static __always_inline void *kmalloc(size_t size, gfp_t flags){struct kmem_cache *cachep;void *ret;if (__builtin_constant_p(size)) {int i = 0;if (!size)return ZERO_SIZE_PTR;#define CACHE(x) \if (size <= x) \goto found; \else \i++;#include <linux/kmalloc_sizes.h>#undef CACHEreturn NULL;found:#ifdef CONFIG_ZONE_DMAif (flags & GFP_DMA)cachep = malloc_sizes[i].cs_dmacachep;else#endifcachep = malloc_sizes[i].cs_cachep;ret = kmem_cache_alloc_trace(size, cachep, flags);return ret;}return __kmalloc(size, flags);}
首先看到的是kmalloc是基于kmem_cache的,这也就是说它是基于slab分配器的。好处是分配粒度可以小于一个页面,例如几十个字节;另外基于cache的分配,效率会很高。
接着看,__builtin_constant_p(exp)是gcc的内建函数,用于判断一个值是否为编译时常数,如果参数exp的值是常数,函数返回 1,否则返回 0。就是说,如果size不是常数,就调用__kmalloc(size, flags);去处理,这里不深入。
再往下,如果size == 0,那就是逗着玩呢,当特殊情况处理,返回ZERO_SIZE_PTR。ZERO_SIZE_PTR的定义如下:
/* * ZERO_SIZE_PTR will be returned for zero sized kmalloc requests. * * Dereferencing ZERO_SIZE_PTR will lead to a distinct access fault. * * ZERO_SIZE_PTR can be passed to kfree though in the same way that NULL can. * Both make kfree a no-op. */#define ZERO_SIZE_PTR ((void *)16)
这让我想起来NULL在Linux中的定义(/usr/include/linux/stddef.h):
#undef NULL#if defined(__cplusplus)#define NULL 0#else#define NULL ((void *)0)#endif上面的定义意思是C++中NULL为0,而Linux C中,NULL为地址0所指向的内容。
而ZERO_SIZE_PTR就是地址16所指向的内容了,其具体会导致什么结果上面的注释都有,咱就不翻译了。
再往下,定义了宏CACHE(x),包含了头文件,又加了分支处理。这里算是见识了Linux内核的高手风范,反正我是头一次见到这种玩法,感觉挺新颖。
把中间的那段代码展开,变成下面这个样子:
#define CACHE(x) \if (size <= x) \goto found; \else \i++;#if (PAGE_SIZE == 4096)CACHE(32)#endifCACHE(64)#if L1_CACHE_BYTES < 64CACHE(96)#endifCACHE(128)#if L1_CACHE_BYTES < 128CACHE(192)#endifCACHE(256)CACHE(512)CACHE(1024)CACHE(2048)CACHE(4096)CACHE(8192)CACHE(16384)CACHE(32768)CACHE(65536)CACHE(131072)#if KMALLOC_MAX_SIZE >= 262144CACHE(262144)#endif#if KMALLOC_MAX_SIZE >= 524288CACHE(524288)#endif#if KMALLOC_MAX_SIZE >= 1048576CACHE(1048576)#endif#if KMALLOC_MAX_SIZE >= 2097152CACHE(2097152)#endif#if KMALLOC_MAX_SIZE >= 4194304CACHE(4194304)#endif#if KMALLOC_MAX_SIZE >= 8388608CACHE(8388608)#endif#if KMALLOC_MAX_SIZE >= 16777216CACHE(16777216)#endif#if KMALLOC_MAX_SIZE >= 33554432CACHE(33554432)#endif#undef CACHEreturn NULL;found:
光看估计不好理解,试着给个值,比如申请size为500字节的内存空间,看看流程怎么走。
那么,对于一般的ARM架构,PAGE_SIZE就是4096,于是先执行CACHE(32)。在CACHE(32)内部执行时,判断500>32,于是i++,i变成1。
接着执行CACHE(64),500>64,于是i++,i变成2。
L1_CACHE_BYTES一般没那么小(大于128字节对于多数情况成立),所以,下面的跳转更常见:
接着执行CACHE(128),500>128,于是i++,i变成3。
接着执行CACHE(256),500>256,于是i++,i变成4。
接着执行CACHE(512),500<=512,于是goto found,i定格在4。
到了found:这里之后,我们看到它区分了DMA和非DMA的情况。我记得DMA物理内存是在固定的低地址空间(低16M物理地址空间),而一般的物理内存(ZONE_NORMAL)在16M~896M之间。
这里又冒出了一个malloc_sizes数组,看看它的定义:
/* * These are the default caches for kmalloc. Custom caches can have other sizes. */struct cache_sizes malloc_sizes[] = {#define CACHE(x) { .cs_size = (x) },#include <linux/kmalloc_sizes.h>CACHE(ULONG_MAX)#undef CACHE};
好吧,还得了解一下cache_sizes是怎么回事:
/* Size description struct for general caches. */struct cache_sizes {size_t cs_size;struct kmem_cache*cs_cachep;#ifdef CONFIG_ZONE_DMAstruct kmem_cache*cs_dmacachep;#endif};extern struct cache_sizes malloc_sizes[];
这下都能对上了,对于我们前面设定的500字节,到了这里,就变成了
cachep = malloc_sizes[4].cs_cachep;
而这个malloc_sizes[]数组展开就是这样的:
struct cache_sizes malloc_sizes[] = {#define CACHE(x) { .cs_size = (x) },{ .cs_size = (32) },{ .cs_size = (64) },{ .cs_size = (128) },{ .cs_size = (256) },{ .cs_size = (512) },...{ .cs_size = (ULONG_MAX) },#undef CACHE};
所以呢,index为4的情况就是.cs_size = (512)。
这样,搞了半天,还只是根据 “申请的size” 找对应的 “应申请size”。
实际的申请还是由最后这句来完成,而它的展开分析貌似更复杂。
ret = kmem_cache_alloc_trace(size, cachep, flags);
kmalloc的使用
说真的,以前还真没仔细想过什么情况下用kmalloc?
后来注意到kmalloc在内核源码中是怎么使用的,才有所感悟。
随便找个例子看看:
int cris_io_interface_register_watcher(void (*notify)(const unsigned int gpio_in_available, const unsigned int gpio_out_available, const unsigned char pa_available, const unsigned char pb_available)){struct watcher *w;(void)cris_io_interface_init();if (NULL == notify) {return -EINVAL;}w = kmalloc(sizeof(*w), GFP_KERNEL);if (!w) {return -ENOMEM;
w是一个指向结构体的指针,为其分配内存空间时使用了kmalloc。为什么不直接这样写?
struct watcher w;这样就是局部变量,分配在内核栈上。可是内核栈空间有限,需要省着用啊。而使用了kmalloc,应该是分配在堆上了,堆的空间大的很,随便用。
再来看一个例子:
static int ahash_op_unaligned(struct ahash_request *req, int (*op)(struct ahash_request *)){struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);unsigned long alignmask = crypto_ahash_alignmask(tfm);unsigned int ds = crypto_ahash_digestsize(tfm);struct ahash_request_priv *priv;int err;priv = kmalloc(sizeof(*priv) + ahash_align_buffer_size(ds, alignmask), (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ? GFP_KERNEL : GFP_ATOMIC);
可见,简单的变量alignmask, ds, err等都是栈上直接分配,而结构体变量就定义个指针再用kmalloc堆上申请。看来基本原则就是为了少占用内核栈。
那么为什么不都在堆上申请?既然堆的空间大。我的理解是栈的效率高啊,“这体现在,有专门的寄存器指向栈所在的地址,有专门的机器指令完成数据入栈出栈的操作。这种机制的特点是效率高,支持的数据有限,一般是整数,指针,浮点数等系统直接支持的数据类型,并不直接支持其他的数据结构”(这段话是网上找的)。“在过去,堆内存管理器是实际的规范,但是其性能会受到内存碎片和内存回收需求的影响”(这段话也是网上找的)。所以呢,简单的需要高速存取的数据分配给栈,别超过了内核栈的限制就行;复杂的数据结构,比如结构体,就用堆分配,即kmalloc;特别大的数据结构,又不需要物理地址连续的(比如链表),用vmalloc分配。
题外话:
记得有面试题问malloc, calloc和realloc什么关系,在/usr/include/malloc.h中发现如下申明:
/* Allocate SIZE bytes of memory. */extern void *malloc (size_t __size) __THROW __attribute_malloc__ __wur;/* Allocate NMEMB elements of SIZE bytes each, all initialized to 0. */extern void *calloc (size_t __nmemb, size_t __size) __THROW __attribute_malloc__ __wur;/* Re-allocate the previously allocated block in __ptr, making the new block SIZE bytes long. *//* __attribute_malloc__ is not used, because if realloc returns the same pointer that was passed to it, aliasing needs to be allowed between objects pointed by the old and new pointers. */extern void *realloc (void *__ptr, size_t __size) __THROW __attribute_warn_unused_result__;/* Free a block allocated by `malloc', `realloc' or `calloc'. */extern void free (void *__ptr) __THROW;/* Free a block allocated by `calloc'. */extern void cfree (void *__ptr) __THROW;看注释就行了,不解释。
- Linux内核学习:kmalloc
- linux内核kmalloc函数使用方法
- 浅析linux内核内存管理之kmalloc
- 浅析linux内核内存管理之kmalloc
- Linux内核 kmalloc, kzalloc & devm_kzalloc 区别
- linux内核Kmalloc - GFP_ATOMIC - GFP_KERNEL - GFP_USER
- LINUX内核内存管理kmalloc,vmalloc
- linux内核kmalloc与vmalloc的区别.
- Linux内核 kmalloc, kzalloc & devm_kzalloc 区别
- Linux内核 kmalloc, kzalloc & devm_kzalloc 区别
- linux 驱动学习之kmalloc 内存分配
- Linux kmalloc
- [linux 内核]kmalloc/kfree,vmalloc/vfree函数用法和区别
- Kmalloc内部实现:挖掘Linux内核内存分配(一)
- kmalloc内核函数
- linux内核Kmalloc分配内存需要注意的问题(GFP_KERNEL可能会造成内核调度错误)
- linux内核Kmalloc分配内存需要注意的问题(GFP_KERNEL可能会造成内核调度错误)
- linux内核__get_free_page,kmalloc,vmalloc的区别,内核对内存的管理
- uC/GUI+STM32(原创)
- 数据挖掘相关
- 设置系统的语言
- VIM常用技巧
- apk破解攻略
- Linux内核学习:kmalloc
- zoj2411-Link Link Look(WA)
- 开源中国在线工具
- 今天用Map集合写了一个字符串字符统计的程序,看集合看的头痛,就看了一下GUI,于是就随便记点。
- 完全用Linux工作
- Sql Sever 2008 jar Maven pom.xml dependency
- 《锋利的jQuery》读书笔记 第1章 认识jQuery
- 2012年第三届西安电子科技大学网络攻防 大赛暨网络渗透部分通关方案 V2.0
- 为什么我们需要uCos