找回密码
 立即注册
Qt开源社区 门户 查看内容

Linux kmalloc的一个细节

2019-9-29 22:00| 发布者: admin| 查看: 592| 评论: 0

摘要: 昨晚上看代码,刚好看到 kmalloc ,刚好看的代码不是最新的,里面有一段代码,如果不看上下文,死活不知道是什么意思,估计也是大神才想出来写这样的代码,既然是大神写的,我想记录下来给大家都了解下。直接上代码/ ...
昨晚上看代码,刚好看到 kmalloc ,刚好看的代码不是最新的,里面有一段代码,如果不看上下文,死活不知道是什么意思,估计也是大神才想出来写这样的代码,既然是大神写的,我想记录下来给大家都了解下。

直接上代码
    /** * 获得普通高速缓存中的对象。 */static inline void *kmalloc(size_t size, int flags){ if (__builtin_constant_p(size)) { int i = 0; /** * 找size对应的几何分布大小值。 */#define CACHE(x) \ if (size <= x) \ goto found; \ else \ i++;#include "kmalloc_sizes.h" /** * 运行到此,说明要分配的对象太大,不能分配这么大的对象。 */#undef CACHE { extern void __you_cannot_kmalloc_that_much(void); __you_cannot_kmalloc_that_much(); } /** * 请求分配的size对应的高速缓存描述符索引号为i * 根据GFP_DMA,在不同的高速缓存描述符中分配对象。 */found: return kmem_cache_alloc((flags & GFP_DMA) ? malloc_sizes[i].cs_dmacachep : malloc_sizes[i].cs_cachep, flags); } return __kmalloc(size, flags);}
    这个是Linux 2.6 里面的 kmalloc代码,最新的kmalloc已经不是这样了。

    好了,我们讨论下,下面这段代码是什么意思呢?如果你没有继续往下看的话,就能看懂这段代码的含义,直接在留言处评论,看我知道关注的读者中大家什么水平
      #define CACHE(x) \if (size <= x) \ goto found; \else \ i++;#include"kmalloc_sizes.h"/** * 运行到此,说明要分配的对象太大,不能分配这么大的对象。 */#undef CACHE

      把宏展开


      既然看不懂,还是要把代码展开看看什么情况
        #define CACHE(x) \ if (size <= x) \ goto found; \ else \ i++;#include "kmalloc_sizes.h" /** * 运行到此,说明要分配的对象太大,不能分配这么大的对象。 */#undef CACHE
        展开后 主要是多了
          #include "kmalloc_sizes.h"
          这里的代码,我们去看看这个包含的是什么代码
            #if (PAGE_SIZE == 4096) CACHE(32)#endif CACHE(64)#if L1_CACHE_BYTES < 64 CACHE(96)#endif CACHE(128)#if L1_CACHE_BYTES < 128 CACHE(192)#endif CACHE(256) CACHE(512) CACHE(1024) CACHE(2048) CACHE(4096) CACHE(8192) CACHE(16384) CACHE(32768) CACHE(65536) CACHE(131072)#ifndef CONFIG_MMU CACHE(262144) CACHE(524288) CACHE(1048576)#ifdef CONFIG_LARGE_ALLOCS CACHE(2097152) CACHE(4194304) CACHE(8388608) CACHE(16777216) CACHE(33554432)#endif /* CONFIG_LARGE_ALLOCS */#endif /* CONFIG_MMU */

            好了,这里我们看到了这个宏的调用的位置了,首先是 PAGE_SIZE == 4096 这个就是页的大小的,页大小默认是 4k ,所以这里就判断页的大小

            假设我们调用进去的 size 大小是 400 这时候调用的是 CACHE(32) 宏展开变成
              #define CACHE(32) \ if (400 <= 32) \ goto found; \ else \ i++;
              这时候执行 i++后 ,i 变成2。然后继续调用CACHE(64) 一直到调用 CACHE(512),size(400) < 512 ,就跑到goto found下面去执行。

              这时候也知道 i的大小了,就可以通过i的大小来获取内存大小。
                return kmem_cache_alloc((flags & GFP_DMA) ? malloc_sizes[i].cs_dmacachep : malloc_sizes[i].cs_cachep, flags);

                __builtin_constant_p(size)


                这个是用来判断常数和非常数的,是GCC里面直接就包含的,我还找到了一个英文的解释,大家可以看看。

                You can use the built-in function __builtin_constant_p to determine if a value is known to be constant at compile time and hence that GCC can perform constant-folding on expressions involving that value. The argument of the function is the value to test. The function returns the integer 1 if the argument is known to be a compile-time constant and 0 if it is not known to be a compile-time constant. A return of 0 does not indicate that the value is not a constant, but merely that GCC cannot prove it is a constant with the specified value of the -O option.

                如果在编译期间就可以知道它的值,就返回1,如果在编译期间不知道它的值,就返回0。

                You typically use this function in an embedded application(嵌入式应用) where memory is a critical resource. If you have some complex calculation(复杂的计算), you may want it to be folded if it involves constants, but need to call a function if it does not. For example:
                  #define Scale_Value(X) \ (__builtin_constant_p (X) \ ? ((X) * SCALE + OFFSET) : Scale (X))

                  You may use this built-in function in either a macro or an inline function. However, if you use it in an inlined function and pass an argument of the function as the argument to the built-in, GCC never returns 1 when you call the inline function with a string constant or compound literal (see Compound Literals) and does not return 1 when you pass a constant numeric value to the inline function unless you specify the -O option.

                  kmalloc 和vmalloc


                  Linux 是按照页来管理内存的

                  在内核里面,我们经常用的两个函数 kmalloc 和vmalloc

                  kmalloc 申请的物理内存和虚拟内存都是连续的, 我们需要连续的物理内存是因为写驱动程序相关的时候,必须要保证连续。但是很多时候是不需要连续的物理内存的。

                   vmalloc函数保证逻辑上的虚拟地址连续 但是物理地址不连续,所以需要TLB这个东西来做映射管理。管理之后就保证虚拟地址连续了。

                  所以,如果我们需要一小块的物理内存,建议就直接用kmalloc

                  如果我们需要非常大的内存,又不要求一定物理内存是连续的话,vmalloc就派上了

                  像内核模块动态申请就用vmalloc

                  除了这两个,还有直接申请页内存的函数,毕竟内存最小的内存单元就是页,函数就不列举了,后面有时间再说,这次就想说那个宏的精妙之处。



                  扫码或长按关注

                  回复「 加群 」进入技术群聊


                  ----------------------------------------------------------------------------------------------------------------------
                  我们尊重原创,也注重分享,文章来源于微信公众号:嵌入式Linux,建议关注公众号查看原文。如若侵权请联系qter@qter.org。
                  ----------------------------------------------------------------------------------------------------------------------

                  鲜花

                  握手

                  雷人

                  路过

                  鸡蛋
                  
                  公告
                  可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了