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
转载时请注明出处和作者联系方式
作者联系方式:张天才 mythfish@gmail.com