前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Kmalloc申请内存源码分析

Kmalloc申请内存源码分析

作者头像
DragonKingZhu
发布2020-04-30 18:16:20
2K0
发布2020-04-30 18:16:20
举报

再上一节了解了SLUB是如何申请一个object的,其中涉及了从当前的freelist申请,以及kmem_cache_cpu->partital链表申请,以及到最后的kmem_cache_cpu→node中申请,如果上述三个步骤都没有申请到的话,就会重新创建一个新的slab,然后设置好freelist的指针,返回object使用。

本节我们重点分析下Kmalloc的实现,其实在驱动中大家使用最多的就是用kmalloc申请内存,kmalloc申请的内存大小都普遍比较小,比较快,而且物理地址和虚拟地址是线性映射的,因为kmalloc拿到的内存是从noraml zone获取的。关于zone的知识我们后面有机会说。

直接上代码,去看下Kmalloc的实现。

代码语言:javascript
复制
* The @flags argument may be one of:
 *
 * %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.
static __always_inline void *kmalloc(size_t size, gfp_t flags)
{
    if (__builtin_constant_p(size)) {
        if (size > KMALLOC_MAX_CACHE_SIZE)
            return kmalloc_large(size, flags);
#ifndef CONFIG_SLOB
        if (!(flags & GFP_DMA)) {
            unsigned int index = kmalloc_index(size);
 
            if (!index)
                return ZERO_SIZE_PTR;
 
            return kmem_cache_alloc_trace(kmalloc_caches[index],
                    flags, size);
        }
#endif
    }
    return __kmalloc(size, flags);
}
  • 申请的时候会传递2个参数,第一个参数就是要申请的大小,第二个参数就是申请内存的一些flag,比如常见的GFP_KERNEL
  • 大家也看下注释都有哪些flag,这些flag都代表啥意思,是否可以睡眠,是否是原子操作等
  • __builtin_constant_p 是gcc的一个属性,我们不用理会,直接看_kmalloc的实现。
代码语言:javascript
复制
#define GFP_ATOMIC  (__GFP_HIGH|__GFP_ATOMIC|__GFP_KSWAPD_RECLAIM)
#define GFP_KERNEL  (__GFP_RECLAIM | __GFP_IO | __GFP_FS)
#define GFP_KERNEL_ACCOUNT (GFP_KERNEL | __GFP_ACCOUNT)
#define GFP_NOWAIT  (__GFP_KSWAPD_RECLAIM)
#define GFP_NOIO    (__GFP_RECLAIM)
#define GFP_NOFS    (__GFP_RECLAIM | __GFP_IO)
#define GFP_USER    (__GFP_RECLAIM | __GFP_IO | __GFP_FS | __GFP_HARDWALL)
#define GFP_DMA     __GFP_DMA
#define GFP_DMA32   __GFP_DMA32
  • 大家常用的应该有GFP_KERNEL,它是由三个flag组成的。__GFP_RECLAIM=可回收,__GFP_IO=有IO的操作,有IO的操作就可能会导致sleep
  • GFP_ATOMIC: 它也是有三个组成,__GFP_HIGH=高优先级等等
  • 大家有兴趣可以看看Gfp.h文件的注释就清楚了。
代码语言:javascript
复制
void *__kmalloc(size_t size, gfp_t flags)
{
    struct kmem_cache *s;
    void *ret;
 
    if (unlikely(size > KMALLOC_MAX_CACHE_SIZE))
        return kmalloc_large(size, flags);
 
    s = kmalloc_slab(size, flags);
 
    if (unlikely(ZERO_OR_NULL_PTR(s)))
        return s;
 
    ret = slab_alloc(s, flags, _RET_IP_);
 
    trace_kmalloc(_RET_IP_, ret, size, s->size, flags);
 
    kasan_kmalloc(s, ret, size, flags);
 
    return ret;
}
  • 如果size大于Kmalloc申请的最大size(4K),则调用kmalloc_large去申请。也就是申请太大的内存,就不用直接找我slab了,直接去找buddy拿吧
  • 通过kmalloc_slab去获取对应大小的kmem_cache缓冲池
  • 调用slab_alloc从对应的kmem_cache中去申请一个object,返回去即可。
  • 所以说来说去,还是从slab去申请内存,里面的内存就和上一节的内存一样了。

如果size大于4K,每个平台,每个版本,每个slab的实现大小不一样。

代码语言:javascript
复制
static __always_inline void *kmalloc_large(size_t size, gfp_t flags)
{
    unsigned int order = get_order(size);
    return kmalloc_order_trace(size, flags, order);
}
 
 
void *kmalloc_order(size_t size, gfp_t flags, unsigned int order)
{
    void *ret;
    struct page *page;
 
    flags |= __GFP_COMP;
    page = alloc_pages(flags, order);
    ret = page ? page_address(page) : NULL;
    kmemleak_alloc(ret, size, 1, flags);
    kasan_kmalloc_large(ret, size, flags);
    return ret;
}

很直接,如果大小太大,不用找我slab了,我是小内存申请分配器,反正我的内存也是从buddy拿的,你直接去找buddy拿对应order的页。

代码语言:javascript
复制
struct kmem_cache *kmalloc_slab(size_t size, gfp_t flags)
{
    unsigned int index;
 
    if (size <= 192) {
        if (!size)
            return ZERO_SIZE_PTR;
 
        index = size_index[size_index_elem(size)];
    } else {
        if (unlikely(size > KMALLOC_MAX_CACHE_SIZE)) {
            WARN_ON(1);
            return NULL;
        }
        index = fls(size - 1);
    }
 
#ifdef CONFIG_ZONE_DMA
    if (unlikely((flags & GFP_DMA)))
        return kmalloc_dma_caches[index];
 
#endif
    return kmalloc_caches[index];
}
  • 根据size计算出一个index,此index就是kmalloc_caches数组下标,此数据中有对应很多个大小的kmalloc_cache缓冲池
  • 如果申请的内存是从DMA ZONE去申请,就从kmalloc_dma_caches中去找对应的kmalloc_cache缓冲池
代码语言:javascript
复制
static void __init new_kmalloc_cache(int idx, slab_flags_t flags)
{
    kmalloc_caches[idx] = create_kmalloc_cache(kmalloc_info[idx].name,
                    kmalloc_info[idx].size, flags, 0,
                    kmalloc_info[idx].size);
}

kmalloc_caches数组是通过kmalloc_info数组里面的信息决定的。

代码语言:javascript
复制
const struct kmalloc_info_struct kmalloc_info[] __initconst = {
    {NULL,                      0},        {"kmalloc-96",             96},
    {"kmalloc-192",           192},        {"kmalloc-8",               8},
    {"kmalloc-16",             16},        {"kmalloc-32",             32},
    {"kmalloc-64",             64},        {"kmalloc-128",           128},
    {"kmalloc-256",           256},        {"kmalloc-512",           512},
    {"kmalloc-1024",         1024},        {"kmalloc-2048",         2048},
    {"kmalloc-4096",         4096},        {"kmalloc-8192",         8192},
    {"kmalloc-16384",       16384},        {"kmalloc-32768",       32768},
    {"kmalloc-65536",       65536},        {"kmalloc-131072",     131072},
    {"kmalloc-262144",     262144},        {"kmalloc-524288",     524288},
    {"kmalloc-1048576",   1048576},        {"kmalloc-2097152",   2097152},
    {"kmalloc-4194304",   4194304},        {"kmalloc-8388608",   8388608},
    {"kmalloc-16777216", 16777216},        {"kmalloc-33554432", 33554432},
    {"kmalloc-67108864", 67108864}
};

可以看到,系统中专门为kmalloc申请了一系列大小的kmem_cache缓冲区,平常用到的大小都会有涉及的。

大家也可以去/proc/slabinfo下去查看对应的kmalloc信息

代码语言:javascript
复制
root:/ # cat /proc/slabinfo | grep "kmalloc"
kmalloc-8192         644    663   8704    3    8 : tunables    0    0    0 : slabdata    221    221      0
kmalloc-4096        2250   2254   4608    7    8 : tunables    0    0    0 : slabdata    322    322      0
kmalloc-2048        6767   6780   2560   12    8 : tunables    0    0    0 : slabdata    565    565      0
kmalloc-1024        2374   2436   1536   21    8 : tunables    0    0    0 : slabdata    116    116      0
kmalloc-512         7034   7296   1024   32    8 : tunables    0    0    0 : slabdata    228    228      0
kmalloc-256        17285  17640    768   21    4 : tunables    0    0    0 : slabdata    840    840      0
kmalloc-128       301624 303775    640   25    4 : tunables    0    0    0 : slabdata  12151  12151      0
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-04-27 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档