我正在看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()检查"尺寸> KMALLOC_MAX_CACHE_SIZE"并在运行时调用kmalloc_large()?
答案 0 :(得分:2)
你的两个问题实际上是单个问题的一部分:
什么是__builtin_constant_p(大小)?
运算符AppSidebarComponent
是特定于gcc的扩展,它检查是否可以在编译时评估其参数。例如,如果你打电话
__builtin_constant_p
然后运算符返回true。
但是
p = kmalloc(100, GFP_KERNEL);
运算符返回false *。
通过知道某些函数的参数在编译时是已知的,可以在编译时检查它,并执行一些优化。
size_t size = 100;
p = kmalloc(size, GFP_KERNEL);
虽然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);
因为编译器无法在编译时预测分支。
“fall-back”函数的实现size_t size = 100000;
__kmalloc(size, GFP_KERNEL);
在任何地方检查其参数,以防无法执行编译时检查。
* - 在我最近的测试中,编译器实际上并没有尝试预测直接用常量赋值的__kmalloc
变量的值。但是在未来的gcc版本中可能会有所改变。