【问题标题】:Large size kmalloc in the linux kernel kmalloclinux内核kmalloc中的大尺寸kmalloc
【发布时间】:2018-02-23 11:32:09
【问题描述】:

我正在查看 Linux 版本 4.9.31

还有一个slab和slub的kmalloc()函数

以下是include/linux/slab.h的kmalloc()函数

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)) {
            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);
}

在上面的代码中,当 __builtin_constant_p(size) 为真时调用 kmalloc_large()。

第一个问题。 __builtin_constant_p(size) 和 kmalloc_large() 是什么关系?不应该在运行时调用 kmalloc_large(),而不是在编译时调用?

以下是mm/slab.c的__kmalloc()和__do_kmalloc()

static __always_inline void *__do_kmalloc(size_t size, gfp_t flags,
                      unsigned long caller)
{
    struct kmem_cache *cachep;
    void *ret;

    cachep = kmalloc_slab(size, flags);
    if (unlikely(ZERO_OR_NULL_PTR(cachep)))
        return cachep;
    ret = slab_alloc(cachep, flags, caller);

    kasan_kmalloc(cachep, ret, size, flags);
    trace_kmalloc(caller, ret, 
              size, cachep->size, flags);

    return ret; 
}

void *__kmalloc(size_t size, gfp_t flags)
{
    return __do_kmalloc(size, flags, _RET_IP_);
}

以下是mm/slub.c的__kmalloc()

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; 
}

第二个问题。 为什么slub __kmalloc() 会在运行时检查“size > KMALLOC_MAX_CACHE_SIZE”并调用kmalloc_large()?

【问题讨论】:

    标签: linux memory-management linux-kernel


    【解决方案1】:

    您的两个问题实际上是单个问题的一部分:

    什么是__builtin_constant_p(size)?

    Operator __builtin_constant_p 是 gcc 特定的扩展,它检查是否可以在编译时评估其参数。例如,如果你打电话

    p = kmalloc(100, GFP_KERNEL);
    

    然后操作符返回true。

    但是有

    size_t size = 100;
    p = kmalloc(size, GFP_KERNEL);
    

    运算符返回 false*。

    通过在编译时知道某个函数的参数,可以在编译时检查它,并进行一些优化。

    if (__builtin_constant_p(size)) {
        if (size > KMALLOC_MAX_CACHE_SIZE)
    

    虽然size > KMALLOC_MAX_CACHE_SIZE 在这里似乎是运行时检查,但它实际上是编译时检查,因为外部条件保证 size 在编译时是已知的。有了这些知识,编译器可以优化出内部分支,如果它为假(如果分支为真,编译器可以优化出其他分支)。

    例如,

    p = kmalloc(100000, GFP_KERNEL);
    

    会编译成

    kmalloc_large(100000, GFP_KERNEL);
    

    p = kmalloc(100, GFP_KERNEL);
    

    会编译成

    __kmalloc(100, GFP_KERNEL);
    

    但是

    size_t size = 100000;
    p = kmalloc(size, GFP_KERNEL);
    

    会编译成

    size_t size = 100000;
    __kmalloc(size, GFP_KERNEL);
    

    因为编译器无法在编译时预测分支。

    “回退”函数__kmalloc 的实现在任何地方检查其参数,以防无法执行编译时检查。


    *- 在我最近的测试中,编译器实际上并没有尝试预测直接分配有常量的size 变量的值。但这可能会在未来的 gcc 版本中改变。

    【讨论】:

    • 我理解你的解释。谢谢。但第二个问题是为什么只能在运行时调用 kmalloc_large() ?检查“size
    猜你喜欢
    • 2013-12-19
    • 2012-08-12
    • 2013-05-03
    • 2021-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多