这里只介绍一下上面代码中的那个__you_cannot_kmalloc_that_much()函数,这个函数实际上并没有定义,但是编译连接(insmod)都不会有问题,是一个很有趣的用法. 先看__builtin_constant_p(),这是gcc的内建函数,可以判断它的参数在编译时是否为常量,若是,返回值为真,否则为假,凡是通过gcc编译的c代码中都可以直接引用这一类以__builtin_打头的函数.下面的include展开的结果如下:
if (size <= 32)
goto found;
else
i++;
if (size <= 64)
goto found;
else
i++;
..............
..............
if (size <= 131072)
goto found;
else
i++;
如果size在编译时就能确定出它的大小,编译器就可以充分发挥他的优化能力,把无关代码删掉,比如size小于131072时,整个函数只剩下一个return语句了,__you_cannot_kmalloc_that_much的调用也被删掉了,所以在连接时不会有问题,否则在insmod模块的时候会告诉你该符号没有定义,这说明你给的参数size大于了128k. 这实际上是在编译连接时做了参数范围的检查,这个用法真得不多见.
kmalloc引入的目的:
内核为了提高一些常用的数据结构(一般大小远小于1个page)的内存使用效率(空间和时间上),引入slab分配器(区别于伙伴系统算法),slab分配器使用伙伴系统算法作为物理内存分的接口,但是对于slab分配器中的对象,内核释放这个对象之后,相应的物理内存并不释放,可以留给下一个可能的对象使用,这样可以提高经常使用的数据结构内存使用效率(否则导致内存碎片和让内核疲劳于不停的内存分配和释放)。对于每种常用的数据结构(比如skb_buff),内核可以使用kmem_cache_create()创建一个专用的高速缓存,但是如果对这个存储区的请求并不是那么的频繁,可以考虑使用kmalloc从通用高速缓存分配按照几何分布的存储区(从32个字节,到128k)。
vmalloc引入的目的:
系统对于物理内存的分配最终通过伙伴系统的alloc_pages实现,一般把存储区映射到一组连续的页框,但是这样无疑会引入外碎片,对于这些外碎片应该是加以合理的利用,于是引入非连续存储区的管理,非连续的存储区中非连续主要是强调物理上的非连续,但是线性地址是连续,这些线性地址是在3G+mem+8M以上地址空间中分配和管理的。
固定映射的线性地址存放在线性地址的第四个GB的末端,它是一个索引对应一个页框及其线性地址,通过索引寻找到这个线性地址,据说对于指针变量效率会更高。
其实kmalloc和vmalloc并没有太多的联系,vmalloc会调用kmalloc分配非连续线性区描述这样的对象,但他们最终都通过伙伴系统算法作为接口分配内存。
* %GFP_USER - Allocate memory on behalf of user. May sleep. * * %GFP_KERNEL - Allocate normal kernel ram. May sleep. * * %GFP_ATOMIC - Allocation will not sleep. May use emergency pools. * For example, use this inside interrupt handlers. * * %GFP_HIGHUSER - Allocate pages from high memory. * * %GFP_NOIO - Do not do any I/O at all while trying to get memory. * * %GFP_NOFS - Do not make any fs calls while trying to get memory. * * %GFP_NOWAIT - Allocation will not sleep. * * %GFP_THISNODE - Allocate node-local memory only. * * %GFP_DMA - Allocation suitable for DMA. * Should only be used for kmalloc() caches. Otherwise, use a * slab created with SLAB_DMA.