HardBirch

Linux内核源码分析——compiler.h分析

时间:11-03-13 栏目:系统技术篇 作者:鲁智森也有文化 评论:0 点击: 1,947 次

 

 

《Linux内核源码分析——compiler.h分析》转自 强悍的lost的bolg

 

Linux的内核源码都会包含文件linux/compile.h,所以先分析该文件内的内容,作为开篇。

1 汇编编译时不定义的内容

该文件的第一个内容是对宏__ASSEMBLY__的判断,这个宏的作用是避免在进行汇编编译的时候,不定义后续相关内容。这个宏通过在编译器中用-D选项中加入,参数AFLAGS也包含该宏定义。在汇编时编译器会定义__ASSEMBLY__为1。
——————————————————————————–

#ifndef __ASSEMBLY__
——————————————————————————–

1.1 Sparse工具检测使用的属性定义

接下来是__CHECKER__宏的判断,__CHECKER__宏在通过Sparse(Semantic Parser for C)工具对内核代码进行检查时会定义的。在使用make C=1或C=2时便会调用该工具,这个工具可以检查在代码中声明了sparse所能检查到的相关属性的内核函数和变量。
——————————————————————————–

#ifdef __CHECKER__
——————————————————————————–

下面分析一下sparse所能检查的相关属性。
——————————————————————————–

# define __user                __attribute__((noderef, address_space(1)))

# define __kernel        /* default address space */

# define __safe                __attribute__((safe))

# define __force        __attribute__((force))

# define __nocast        __attribute__((nocast))

# define __iomem        __attribute__((noderef, address_space(2)))

# define __acquires(x)        __attribute__((context(x,0,1)))

# define __releases(x)        __attribute__((context(x,1,0)))

# define __acquire(x)        __context__(x,1)

# define __release(x)        __context__(x,-1)

# define __cond_lock(x,c)        ((c) ? ({ __acquire(x); 1; }) : 0)

extern void __chk_user_ptr(const volatile void __user *);

extern void __chk_io_ptr(const volatile void __iomem *);
——————————————————————————–

__user特性用来修饰一个变量的地址,该变量必须是非解除参考(no dereference)即地址是有效的,并且变量所在的地址空间必须为1,这里为(address_space(1)),用户地址空间。sparse把 地址空间分为3部分,0表示普通地址空间,对内核来说就是地址空间。1表示用户地址空间。2表示设备地址映射空间,即设备寄存器的地址空间。

__kernel特性修饰变量为内核地址,为内核代码里面默认的地址空间。

__safe特性声明该变量为安全变量,这是为了避免在内核函数未对传入的参数进行校验就使用的情况下,会导致编译器对其报错或输出告警信息。        通过该特性说明该变量不可能为空。

__force特性声明该变量是可以强制类型转换的。

__nocast声明该变量参数类型与实际参数类型要一致才可以。

__iomem声明地址空间是设备地址映射空间,其他的与__user一样。

__acquires为函数属性定义的修饰,表示函数内,该参数的引用计数值从1变为0。

__releases与__acquires相反,这一对修饰符用于Sparse在静态代码检测时,检查调用的次数和匹配请求,经常用于检测lock的获取和释放。

__acquire表示增加变量x的计数,增加量为1。

__release表示减少变量x的计数,减少量为1。这一对与上面的那一对是一样,只是这一对用在函数的执行过程中,都用于检查代码中出现不平衡的状况。

__cond_lock用于表示条件锁,当c这个值不为0时,计数值加1,并返回1。

__chk_user_ptr和__chk_io_ptr在这里只声明函数,没有函数体,目的就是在编译过程中Sparse能够捕捉到编译错误,检查参数的类型。

1.2 内核模块的宏定义
接下来是宏__KERNEL__的判断,后续部分的内容是内核模块相关的宏定义,不用于用户进程的代码。


#ifdef __GNUC__
#include <linux/compiler-gcc.h>
#endif

 


这个部分说明,如果采用的是GNU C的编译器,包含gcc相关的头文件compiler-gcc.h。这个文件的分析在后续的文章中详细描述

 


#define notrace __attribute__((no_instrument_function))

 


宏notrace的定义,这个宏用于修饰函数,说明该函数不被跟踪。这里所说的跟踪是gcc一个很重要的特性,只要在编译时打开相关的跟踪选择,编译器会 加入一些特性,使得程序在执行完后,可以通过工具(如Graphviz)来查看函数的调用过程。而对于内核来说,内部采用了ftrace机制,而不采用 trace的特性。

 


#ifdef __INTEL_COMPILER
# include <linux/compiler-intel.h>
#endif

 


__INTEL_COMPILER宏的判断,用于在使用INTEL编译器时,包含intel相关的头文件compiler-intel.h。这个文件不做展开说明。

 

上面包含的文件compiler-gcc.h和compiler-intel.h里面定义了编译器相关的内容,下面开始定义通用的一些编译属性。


struct ftrace_branch_data {
const char *func;
const char *file;
unsigned line;
union   {
struct {
unsigned long correct;
unsigned long incorrect;
};
struct {
unsigned long miss;
unsigned long hit;
};
unsigned long miss_hit[2];
};
};

 

/*
* Note: DISABLE_BRANCH_PROFILING can be used by special lowlevel code
* to disable branch tracing on a per file basis.
*/
#if defined(CONFIG_TRACE_BRANCH_PROFILING) /
&& !defined(DISABLE_BRANCH_PROFILING) && !defined(__CHECKER__)
void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect);

#define likely_notrace(x) __builtin_expect(!!(x), 1)
#define unlikely_notrace(x) __builtin_expect(!!(x), 0)

#define __branch_check__(x, expect) ({     /
int ______r;     /
static struct ftrace_branch_data  /
__attribute__((__aligned__(4)))  /
__attribute__((section(“_ftrace_annotated_branch”))) /
______f = {    /
.func = __func__,   /
.file = __FILE__,   /
.line = __LINE__,   /
};      /
______r = likely_notrace(x);   /
ftrace_likely_update(&______f, ______r, expect); /
______r;     /
})

/*
* Using __builtin_constant_p(x) to ignore cases where the return
* value is always the same.  This idea is taken from a similar patch
* written by Daniel Walker.
*/
# ifndef likely
#  define likely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 1))
# endif
# ifndef unlikely
#  define unlikely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 0))
# endif

#ifdef CONFIG_PROFILE_ALL_BRANCHES
/*
* “Define ‘is’”, Bill Clinton
* “Define ‘if’”, Steven Rostedt
*/
#define if(cond, …) __trace_if( (cond , ## __VA_ARGS__) )
#define __trace_if(cond) /
if (__builtin_constant_p((cond)) ? !!(cond) :   /
({        /
int ______r;      /
static struct ftrace_branch_data   /
__attribute__((__aligned__(4)))   /
__attribute__((section(“_ftrace_branch”))) /
______f = {     /
.func = __func__,   /
.file = __FILE__,   /
.line = __LINE__,   /
};      /
______r = !!(cond);     /
______f.miss_hit[______r]++;     /
______r;      /
}))
#endif /* CONFIG_PROFILE_ALL_BRANCHES */

#else
# define likely(x) __builtin_expect(!!(x), 1)
# define unlikely(x) __builtin_expect(!!(x), 0)
#endif


对(defined(CONFIG_TRACE_BRANCH_PROFILING) && !defined(DISABLE_BRANCH_PROFILING) && !defined(__CHECKER__)   )这个三个值的检查,确认likely和unlikely是否需要使用branch tracer跟踪。branch tracer是ftrace的一种trace功能。主要用于跟踪likely预测的正确率。为了实现这个功能,branch tracer重新定义了likely和unlikely。

 

CONFIG_TRACE_BRANCH_PROFILING宏在配置内核时开启。DISABLE_BRANCH_PROFILING宏只在低级代码关闭基于每个文件的分支跟踪。!defined(__CHECKER__)说明在未使用Sparse工具时有效。

struct ftrace_branch_data结构体用于记录ftrace branch的trace记录。

likely_notrace和unlikely_notrace宏使用__builtin_expect函数,__builtin_expect告诉编译器程序设计者期望的比较结果,以便编译器对代码进行优化,改变汇编代码中的判断跳转语句。

__branch_check__宏,记录当前trace点,并利用ftrace_likely_update记录likely判断的正确性,并将结果保 存在ring buffer中,之后用户可以通过ftrace的debugfs接口读取分支预测的相关信息。从而优调整代码,优化性能。

重定义likely和unlikely宏里面用到了GCC的build-in函数__builtin_constant_p判断一个表达式在编译时是否为 常量。当是常量时,直接返回likely和unlikely表达式的值,没必要做预测的记录。当表达式为非常数时,使用宏 __branch_check__检查分支并记录likely判断的预测信息。

如果设置了CONFIG_PROFILE_ALL_BRANCHES宏,将重定义if()为__trace_if。__trace_if检查if的所有分支,并记录分支的跟踪信息。

——————————————————————————–

/* Optimization barrier */
#ifndef barrier
# define barrier() __memory_barrier()
#endif

——————————————————————————–

这里表示如果没有定义barrier函数,则定义barrier()函数为__memory_barrier()。但在内核代码会包含 compiler-gcc.h,在里面定义了barrier()为__asm__ __volatile__(“”: : :”memory”)。barrier是屏障的意思。因为CPU在执行的过程中,为了优化指令,会对部分指令进行优化执行,执行的顺序并不一定按照程序在 源码内写的顺序。编译器也有可能在生成二进制指令的时候,也进行一些优化。有的时候可能在多CPU,多线程或是互斥锁执行中遇到问题。编译器利用内存屏障 保证屏障以上的操作,不会影响到屏障以下的操作,避免优化造成的执行顺序不一致。__asm__表示后面的东西都是汇编指令,当然,这是一种在C语言中嵌 入汇编的方法,语法有其特殊 性,我在这里只讲跟这条指令有关的。__volatile__表示不对此处的汇编指令做优化。””表示空指令,由于没有输出,两个冒号来代替输出操作数的 位置,后面一个冒号分割输入,再加上空的输入,即为”” : : :。memory是gcc中的一个特殊的语法,加上它,gcc编译器则会产生一个动作,使gcc不保留在寄存器内内存的值,并且对相应的内存不会做存储与 加载的优化处理。

——————————————————————————–

#ifndef RELOC_HIDE
# define RELOC_HIDE(ptr, off)     /
({ unsigned long __ptr;     /
__ptr = (unsigned long) (ptr);    /
(typeof(ptr)) (__ptr + (off)); })
#endif

2 内核代码有效的定义

第一个部分介绍了汇编代码不使用的定义。下面开始介绍一下对内核代码有效的定义。

/*
* Allow us to mark functions as ‘deprecated’ and have gcc emit a nice
* warning for each use, in hopes of speeding the functions removal.
* Usage is:
*   int __deprecated foo(void)
*/
#ifndef __deprecated
# define __deprecated  /* unimplemented */
#endif


函数标记为deprecated,gcc在编译时会产生警告,提示尽快把这个函数移除。实际上deperated已经在compiler-gcc.h中定义了,如果有包含它,在这里将不会再定义。

 


#define __deprecated   __attribute__((deprecated))

 


__deprecated通过gcc的__attribute__((deprecated))告诉gcc被标记对象的属性。

 

#ifdef MODULE
#define __deprecated_for_modules __deprecated
#else
#define __deprecated_for_modules
#endif


这里定义了MODULE的deprecated属性。

 


#ifndef __must_check
#define __must_check
#endif

 


__must_check宏的作用是告诉编译器当调用者调用这个函数时不使用返回结果产生一个告警。这个宏在compiler-gccx.h中定义了。使用了属性warn_unused_result。

 


#ifndef CONFIG_ENABLE_MUST_CHECK
#undef __must_check
#define __must_check
#endif
#ifndef CONFIG_ENABLE_WARN_DEPRECATED
#undef __deprecated
#undef __deprecated_for_modules
#define __deprecated
#define __deprecated_for_modules
#endif

 


这里定义了内核配置的选项,控制__must_check和__deprecated的开关。

 


/*
* Allow us to avoid ‘defined but not used’ warnings on functions and data,
* as well as force them to be emitted to the assembly file.
*
* As of gcc 3.4, static functions that are not marked with attribute((used))
* may be elided from the assembly file.  As of gcc 3.4, static data not so
* marked will not be elided, but this may change in a future gcc version.
*
* NOTE: Because distributions shipped with a backported unit-at-a-time
* compiler in gcc 3.3, we must define __used to be __attribute__((used))
* for gcc >=3.3 instead of 3.4.
*
* In prior versions of gcc, such functions and data would be emitted, but
* would be warned about except with attribute((unused)).
*
* Mark functions that are referenced only in inline assembly as __used so
* the code is emitted even though it appears to be unreferenced.
*/
#ifndef __used
# define __used   /* unimplemented */
#endif

 

#ifndef __maybe_unused
# define __maybe_unused  /* unimplemented */
#endif


这个部分主要介绍了一下 函数和变量“定义却未使用”的情况下,编译器是否将其编译到目标汇编中。gcc采用used属性来告诉编译器,这个函数或变量是有用的,需要编译进汇编, 避免被忽略。由于历史的原因,不同版本gcc对未引用的函数和变量是否会被忽略的处理不一样。

 


#ifndef noinline
#define noinline
#endif

 

/*
* Rather then using noinline to prevent stack consumption, use
* noinline_for_stack instead.  For documentaiton reasons.
*/
#define noinline_for_stack noinline

#ifndef __always_inline
#define __always_inline inline
#endif


inline属性,关系到编译器是否会对比较小的函数进行内联优化。如果你不想编译器这么做,可以采用noinline属性告诉编译器,不需要内联。

 

3 通用的定义

前面两个部分介绍了汇编无效的定义和仅内核有效的定义,最后这部分定义了通用的定义,对汇编,内核代码和应用代码都有用。


/*
* From the GCC manual:
*
* Many functions do not examine any values except their arguments,
* and have no effects except the return value.  Basically this is
* just slightly more strict class than the `pure’ attribute above,
* since function is not allowed to read global memory.
*
* Note that a function that has pointer arguments and examines the
* data pointed to must _not_ be declared `const’.  Likewise, a
* function that calls a non-`const’ function usually must not be
* `const’.  It does not make sense for a `const’ function to return
* `void’.
*/
#ifndef __attribute_const__
# define __attribute_const__ /* unimplemented */
#endif

 

/*
* Tell gcc if a function is cold. The compiler will assume any path
* directly leading to the call is unlikely.
*/

#ifndef __cold
#define __cold
#endif

/* Simple shorthand for a section definition */
#ifndef __section
# define __section(S) __attribute__ ((__section__(#S)))
#endif

/*
* Prevent the compiler from merging or refetching accesses.  The compiler
* is also forbidden from reordering successive instances of ACCESS_ONCE(),
* but only when the compiler is aware of some particular ordering.  One way
* to make the compiler aware of ordering is to put the two invocations of
* ACCESS_ONCE() in different C statements.
*
* This macro does absolutely -nothing- to prevent the CPU from reordering,
* merging, or refetching absolutely anything at any time.  Its main intended
* use is to mediate communication between process-level code and irq/NMI
* handlers, all running on the same CPU.
*/
#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))


__attribute_const__在compiler-gcc.h里面定义为属性__const__,该属性的作用是告诉编译器,这个函数是无状 态,它的输出完全依赖于它的输入。对于参数是指针,如果使用了指针指向的内容,也不能定义为__const__属性。

 

__cold属性只有gcc4.3以上的版本支持,作用是告诉编译器,这个函数可能不怎么用到,编译器会将这一类的函数放在速度低些的某个子section里面,而把非 __cold函数放在速度比较优的section里。

__section没什么好说的就是section属性的一个简易宏。

ACCESS_ONCE就是对这个变量取一次值,他采用了volatile,就使得所有访问该变量时都会从变量的地址中重新获取,而不会用缓存的值。但对于底层CPU来说,这个宏不起任何作用。主要是为了协调进程级别的代码和IRQ中断代码间的变量值的一致性。

 

声明: 本文由( 鲁智森也有文化 )原创编译,转载请保留链接: Linux内核源码分析——compiler.h分析

Linux内核源码分析——compiler.h分析:等您坐沙发呢!

发表评论


QQ群互动

Linux系统与内核学习群:194051772

WP建站技术学习交流群:194062106

魔豆之路QR

魔豆的Linux内核之路

魔豆的Linux内核之路

优秀工程师当看优秀书籍

优秀程序员,要看优秀书!

赞助商广告

友荐云推荐