当涉及到Linux内核中定义的一些宏函数时,我可以发现一些奇怪的三元运算符,其条件是永远为真(1)
#define to_cpumask(bitmap) \
((struct cpumask *)(1 ? (bitmap) \
: (void *)sizeof(__check_is_bitmap(bitmap))))
static inline int __check_is_bitmap(const unsigned long *bitmap)
{
return 1;
}
看来 __check_is_bitmap(bitmap)
函数不会在任何条件下被调用,因为它是用三元运算符条件1写的。
看来为了在编译时检查位图的类型,这个宏函数特意引入了一个函数。__check_is_bitmap
,需要一个无符号长指针参数,尽管它不会被调用。
如果它想检查参数类型的要求,那么使用内联函数或者普通函数会更好。为什么在这种情况下,内核程序员更喜欢使用#defined函数而不是内联函数呢?
我并不是一个内核专家,这里有我的两点意见。
在我看来这像是对宏参数的类型安全检查。bitmap
. 由于 macros
没有函数的类型安全检查,操作符的第3个操作数被用于此目的。
__check_is_bitmap(bitmap)
是操作数的 sizeof
所以它是在未 未评价的语境. 这意味着它永远不会被调用。接下来,然后是 sizeof
转换为 (void *)
并被丢弃。
这将检查两件事。
__check_is_bitmap(bitmap)
是有效的。如果 bitmap
可转换为 const unsigned long *
该类 bitmap
和 void *
有一个共同的类型(条件运算符要求)
如果要检查参数类型的要求,那么使用内联函数或者直接使用普通函数会更好。为什么内核程序员在这种情况下更喜欢使用#defined函数而不是内联函数?
inline
在linux内核中,#defined函数是被禁止的。这在 "Inlining in linux" from kernel.org. 2006年的一篇文章)。
[在linux内核]我们不使用 "inline "关键字 因为它是坏的。
[...]
理论上,这样做的关键字是 "inline"。实际上,这个关键字在新版本的gcc上已经被破坏了。当前版本的gcc把 "inline "变成了一个请求(类似于以前的 "register "关键字),使它基本上失去了作用。这些版本的gcc可以自由地忽略任何对内联函数的请求,以及对没有关键字的内联函数的请求。
这样做的问题是,为了使内联函数有效,内联函数必须在头文件中定义(因为内联是由编译器而不是链接器完成的,所以必须在源代码而不是对象文件中完成)。但即使一个函数的每一次使用都被内联,一个未使用的非内联版本的函数仍然可以被生成并链接,"以防万一"。如果多个.c文件#包含同一个头,这甚至会导致同一个函数的多个非内联版本被传递给链接器。
早期通过声明函数为 "static inline "或 "extern inline"(指示编译器永远不要发出函数的非内联版本,必要时中断构建,以检测编译器是否不遵循指令)来解决这一问题的尝试,曾在一段时间内奏效,但又被新版本的gcc所破坏。
然而,现代的准则是 鼓励使用简短的内联功能:
Linux内核的编码方式
12) 宏、枚举和RTL。
一般来说,内联函数优于类似函数的宏。
15)内联病
似乎有一种常见的误解,认为gcc有一个神奇的 "让我更快 "的加速选项,叫做内联。虽然内联的使用可能是合适的(例如作为替换宏的手段,见第12章),但它通常不是[...]。
一个合理的经验法则是不要在函数中包含超过3行代码的地方使用内联。
所以它看起来像 __check_is_bitmap
昔年 inline
是不允许的,最近也没人改。注意这只是猜测。