`
ovn47ovn
  • 浏览: 13609 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

驱动程序用到的头文件linux/include/linux/compiler.h(基于linux version:2.6.34; arch:arm)

 
阅读更多

驱动程序用到的头文件linux/include/linux/compiler.h(基于linux version:2.6.34; arch:arm)
2010年09月16日
  让我们从一个最简单的驱动程序开始,下面是来自《LINUX设备驱动开发详解》中的一个内核模块程序例子: /*================================================ ====================== A kernel module: book This example is to introduce module params The initial developer of the original code is Baohua Song . All Rights Reserved. ================================================== ====================*/ #include  #include  MODULE_LICENSE("Dual BSD/GPL"); static char *book_name = "dissecting Linux Device Driver"; static int num = 4000; static int book_init(void) { printk(KERN_INFO " book name:%s\n",book_name); printk(KERN_INFO " book num:%d\n",num); return 0; } static void book_exit(void) { printk(KERN_INFO " Book module exit\n "); } module_init(book_init); module_exit(book_exit); module_param(num, int, S_IRUGO); module_param(book_name, charp, S_IRUGO); MODULE_AUTHOR("Song Baohua, author@linuxdriver.cn"); MODULE_DESCRIPTION("A simple Module for testing module params"); MODULE_VERSION("V1.0"); 可以看到例子程序开始包含了头文件linux/init.h,由基本常识可知该文件在linux源码中的绝对路径是linux/include/linux/init.h(linux初始化模块函数定义和defines)。现在看一下该文件的具体内容:  1 #ifndef _LINUX_INIT_H 2 #define _LINUX_INIT_H 3 4 #include  5 以下代码暂时略去 ... ... 该头文件首先包含了另一个头文件linux/compiler.h,依旧由基本常识可知该文件在linux源码中的绝对路径是linux/include/linux/compiler.h。先看一下该文件的内容,回头再来看init.h。  1 #ifndef __LINUX_COMPILER_H 2 #define __LINUX_COMPILER_H 3 4 #ifndef __ASSEMBLY__ 5 6 #ifdef __CHECKER__ 7 # define __user __attribute__((noderef, address_space(1))) 8 # define __kernel __attribute__((address_space(0))) 9 # define __safe __attribute__((safe)) 10 # define __force __attribute__((force)) 11 # define __nocast __attribute__((nocast)) 12 # define __iomem __attribute__((noderef, address_space(2))) 13 # define __acquires(x) __attribute__((context(x,0,1))) 14 # define __releases(x) __attribute__((context(x,1,0))) 15 # define __acquire(x) __context__(x,1) 16 # define __release(x) __context__(x,-1) 17 # define __cond_lock(x,c) ((c) ? ({ __acquire(x); 1; }) : 0) 18 # define __percpu __attribute__((noderef, address_space(3))) 19 extern void __chk_user_ptr(const volatile void __user *); 20 extern void __chk_io_ptr(const volatile void __iomem *); 21 #else 22 # define __user 23 # define __kernel 24 # define __safe 25 # define __force 26 # define __nocast 27 # define __iomem 28 # define __chk_user_ptr(x) (void)0 29 # define __chk_io_ptr(x) (void)0 30 # define __builtin_warning(x, y...) (1) 31 # define __acquires(x) 32 # define __releases(x) 33 # define __acquire(x) (void)0 34 # define __release(x) (void)0 35 # define __cond_lock(x,c) (c) 36 # define __percpu 37 #endif 38 39 #ifdef __KERNEL__ 40 41 #ifdef __GNUC__ 42 #include  43 #endif 44 45 #define notrace __attribute__((no_instrument_function)) 46 47 /* Intel compiler defines __GNUC__. So we will overwrite implementations 48 * coming from above header files here 49 */ 50 #ifdef __INTEL_COMPILER 51 # include  52 #endif 53 54 /* 55 * Generic compiler-dependent macros required for kernel 56 * build go below this comment. Actual compiler/compiler version 57 * specific implementations come from the above header files 58 */ 59 60 struct ftrace_branch_data { 61 const char *func; 62 const char *file; 63 unsigned line; 64 union { 65 struct { 66 unsigned long correct; 67 unsigned long incorrect; 68 }; 69 struct { 70 unsigned long miss; 71 unsigned long hit; 72 }; 73 unsigned long miss_hit[2]; 74 }; 75 }; 76 77 /* 78 * Note: DISABLE_BRANCH_PROFILING can be used by special lowlevel code 79 * to disable branch tracing on a per file basis. 80 */ 81 #if defined(CONFIG_TRACE_BRANCH_PROFILING) \ 82 && !defined(DISABLE_BRANCH_PROFILING) && !defined(__CHECKER__) 83 void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect); 84 85 #define likely_notrace(x) __builtin_expect(!!(x), 1) 86 #define unlikely_notrace(x) __builtin_expect(!!(x), 0) 87 88 #define __branch_check__(x, expect) ({ \ 89 int ______r; \ 90 static struct ftrace_branch_data \ 91 __attribute__((__aligned__(4))) \ 92 __attribute__((section("_ftrace_annotated_branch") )) \ 93 ______f = { \ 94 .func = __func__, \ 95 .file = __FILE__, \ 96 .line = __LINE__, \ 97 }; \ 98 ______r = likely_notrace(x); \ 99 ftrace_likely_update(&______f, ______r, expect); \ 100 ______r; \ 101 }) 102 103 /* 104 * Using __builtin_constant_p(x) to ignore cases where the return 105 * value is always the same. This idea is taken from a similar patch 106 * written by Daniel Walker. 107 */ 108 # ifndef likely 109 # define likely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 1)) 110 # endif 111 # ifndef unlikely 112 # define unlikely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 0)) 113 # endif 114 115 #ifdef CONFIG_PROFILE_ALL_BRANCHES 116 /* 117 * "Define 'is'", Bill Clinton 118 * "Define 'if'", Steven Rostedt 119 */ 120 #define if(cond, ...) __trace_if( (cond , ## __VA_ARGS__) ) 121 #define __trace_if(cond) \ 122 if (__builtin_constant_p((cond)) ? !!(cond) : \ 123 ({ \ 124 int ______r; \ 125 static struct ftrace_branch_data \ 126 __attribute__((__aligned__(4))) \ 127 __attribute__((section("_ftrace_branch"))) \ 128 ______f = { \ 129 .func = __func__, \ 130 .file = __FILE__, \ 131 .line = __LINE__, \ 132 }; \ 133 ______r = !!(cond); \ 134 ______f.miss_hit[______r]++; \ 135 ______r; \ 136 })) 137 #endif /* CONFIG_PROFILE_ALL_BRANCHES */ 138 139 #else 140 # define likely(x) __builtin_expect(!!(x), 1) 141 # define unlikely(x) __builtin_expect(!!(x), 0) 142 #endif 143 144 /* Optimization barrier */ 145 #ifndef barrier 146 # define barrier() __memory_barrier() 147 #endif 148 149 /* Unreachable code */ 150 #ifndef unreachable 151 # define unreachable() do { } while (1) 152 #endif 153 154 #ifndef RELOC_HIDE 155 # define RELOC_HIDE(ptr, off) \ 156 ({ unsigned long __ptr; \ 157 __ptr = (unsigned long) (ptr); \ 158 (typeof(ptr)) (__ptr + (off)); }) 159 #endif 160 161 #endif /* __KERNEL__ */ 162 163 #endif /* __ASSEMBLY__ */ 164 165 #ifdef __KERNEL__ 166 /* 167 * Allow us to mark functions as 'deprecated' and have gcc emit a nice 168 * warning for each use, in hopes of speeding the functions removal. 169 * Usage is: 170 * int __deprecated foo(void) 171 */ 172 #ifndef __deprecated 173 # define __deprecated /* unimplemented */ 174 #endif 175 176 #ifdef MODULE 177 #define __deprecated_for_modules __deprecated 178 #else 179 #define __deprecated_for_modules 180 #endif 181 182 #ifndef __must_check 183 #define __must_check 184 #endif 185 186 #ifndef CONFIG_ENABLE_MUST_CHECK 187 #undef __must_check 188 #define __must_check 189 #endif 190 #ifndef CONFIG_ENABLE_WARN_DEPRECATED 191 #undef __deprecated 192 #undef __deprecated_for_modules 193 #define __deprecated 194 #define __deprecated_for_modules 195 #endif 196 197 /* 198 * Allow us to avoid 'defined but not used' warnings on functions and data, 199 * as well as force them to be emitted to the assembly file. 200 * 201 * As of gcc 3.4, static functions that are not marked with attribute((used)) 202 * may be elided from the assembly file. As of gcc 3.4, static data not so 203 * marked will not be elided, but this may change in a future gcc version. 204 * 205 * NOTE: Because distributions shipped with a backported unit-at-a-time 206 * compiler in gcc 3.3, we must define __used to be __attribute__((used)) 207 * for gcc >=3.3 instead of 3.4. 208 * 209 * In prior versions of gcc, such functions and data would be emitted, but 210 * would be warned about except with attribute((unused)). 211 * 212 * Mark functions that are referenced only in inline assembly as __used so 213 * the code is emitted even though it appears to be unreferenced. 214 */ 215 #ifndef __used 216 # define __used /* unimplemented */ 217 #endif 218 219 #ifndef __maybe_unused 220 # define __maybe_unused /* unimplemented */ 221 #endif 222 223 #ifndef __always_unused 224 # define __always_unused /* unimplemented */ 225 #endif 226 227 #ifndef noinline 228 #define noinline 229 #endif 230 231 /* 232 * Rather then using noinline to prevent stack consumption, use 233 * noinline_for_stack instead. For documentaiton reasons. 234 */ 235 #define noinline_for_stack noinline 236 237 #ifndef __always_inline 238 #define __always_inline inline 239 #endif 240 241 #endif /* __KERNEL__ */ 242 243 /* 244 * From the GCC manual: 245 * 246 * Many functions do not examine any values except their arguments, 247 * and have no effects except the return value. Basically this is 248 * just slightly more strict class than the `pure' attribute above, 249 * since function is not allowed to read global memory. 250 * 251 * Note that a function that has pointer arguments and examines the 252 * data pointed to must _not_ be declared `const'. Likewise, a 253 * function that calls a non-`const' function usually must not be 254 * `const'. It does not make sense for a `const' function to return 255 * `void'. 256 */ 257 #ifndef __attribute_const__ 258 # define __attribute_const__ /* unimplemented */ 259 #endif 260 261 /* 262 * Tell gcc if a function is cold. The compiler will assume any path 263 * directly leading to the call is unlikely. 264 */ 265 266 #ifndef __cold 267 #define __cold 268 #endif 269 270 /* Simple shorthand for a section definition */ 271 #ifndef __section 272 # define __section(S) __attribute__ ((__section__(#S))) 273 #endif 274 275 /* Are two types/vars the same type (ignoring qualifiers)? */ 276 #ifndef __same_type 277 # define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b)) 278 #endif 279 280 /* Compile time object size, -1 for unknown */ 281 #ifndef __compiletime_object_size 282 # define __compiletime_object_size(obj) -1 283 #endif 284 #ifndef __compiletime_warning 285 # define __compiletime_warning(message) 286 #endif 287 #ifndef __compiletime_error 288 # define __compiletime_error(message) 289 #endif 290 291 /* 292 * Prevent the compiler from merging or refetching accesses. The compiler 293 * is also forbidden from reordering successive instances of ACCESS_ONCE(), 294 * but only when the compiler is aware of some particular ordering. One way 295 * to make the compiler aware of ordering is to put the two invocations of 296 * ACCESS_ONCE() in different C statements. 297 * 298 * This macro does absolutely -nothing- to prevent the CPU from reordering, 299 * merging, or refetching absolutely anything at any time. Its main intended 300 * use is to mediate communication between process-level code and irq/NMI 301 * handlers, all running on the same CPU. 302 */ 303 #define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) 304 305 #endif /* __LINUX_COMPILER_H */ 306  所有的内核代码,基本都包含了linux\compile.h这个文件,所以它是基础。首先印入眼帘的是对__ASSEMBLY__这个宏的判断,谈到这个宏涉及到GNU编译器。先提出这样一个问题,记得有个汇编文件名字叫做head.S,假如其被更名为head.s,也就是仅仅把后缀改为小写的s,编译即会出错。先来解释下这是为什么。
  众所周知,head.S的代码主要是汇编编写,理所当然的我们知道汇编代码应该有汇编编译器GNU AS来处理,而GNU AS是不能做宏处理和文件包含处理的。不巧的是head.S中有很多预处理,现在要处理这些宏和文件包含,只有交给CPP预处理器来做。CPP如何识别这些文件呢?答案就是通过文件的后缀名。在命令行下执行man gcc可以找到下面的描述: For any given input file, the file name suffix determines what kind of compilation is done: file.c C source code which must be preprocessed. ... file.s Assembler code. file.S Assembler code which must be preprocessed. 这就是为什么用.S而不是.s的原因了。也就是说由于GNU AS的预处理器无法完成head.S中的宏替换和文件包含功能,只能借助C预处理器来实现,从而提高ARM汇编的程序设计环境,更加方便。
  现在回过头来说宏__ASSEMBLY__,由于借助里C预处理器的功能,而因为ARM汇编和C在宏替换的细节上有所不同,为了区分,引入了__ASSEMBLY__这个变量,这是通过Makefile中的AFLAGS来引入的:
  AFLAGS := -D__ASSEMBLY__$(CPPFLAGS)
  在编译汇编文件时,加入AFLAGS选项,所以__ASSEMBLY__传入,也就是定义了__ASSEMBLY__;在编译C文件时,没有用AFLAGS选项,自如也就没有定义__ASSEMBLY__。
  现在知道这个变量实际是在编译汇编代码的时候,由编译器使用-D这样的参数加进去的,gcc会把这个宏定义为1。用在这里,是因为汇编代码里,不会用到类似于__user这样的属性,因为这样的属性是在定义函数参数的时候加的,这样避免不必要的宏在编译汇编代码时候的引用。
  接下来又一个宏__CHECKER__的判断。当 编译内核代码的时候,使用make C=1或C=2的时候,会调用一个叫Sparse的工具(可以参考文档linux/Documentation/sparse.txt),这个工具对内核代码进行检查,怎么检查呢,就是靠对那些声明过的Sparse这个工具所能识别的特性的内核函数或是变量进行检查。在调用Sparse这个工具的同时,在Sparse代码里,会加上#define __CHECKER__ 1的字样。换句话说,就是,如果使用Sparse对代码进行检查,那么内核代码就会定义__CHECKER__宏,否则就不定义。而这里定义的类似于__attribute__((noderef, address_space(1)))这样的属性就是Sparse这个工具所能识别的了。
  那么这些个属性是干什么用的呢: 
  # define __user __attribute__((noderef, address_space(1))) 
  __user这个特性,即__attribute__((noderef, address_space(1))),是用来修饰一个变量的,这个变量必须是非解除参考(no dereference)的,即这个变量地址必须是有效的,而且变量所在的地址空间类型必须是1,即用户程序空间的。refer to http://lkml.org/lkml/2004/9/12/249
  程序空间被分成了3个部分,0表示normal space,即普通地址空间,对内核代码来说,当然就是内核空间地址了。1表示用户地址空间,这个不用多讲,还有一个2,表示是设备地址映射空间,例如硬件设备的寄存器在内核里所映射的地址空间。所以在内核函数里,有一个copy_to_user的函数,函数的参数定义就使用了这种方式。当然,这种特性检查,只有当机器上安装了Sparse这个工具,而且进行了编译的时候调用,才能起作用的。
  # define __kernel /* default address space */ 
  根据定义,就是默认的地址空间,即0,我想定义成__attribute__((noderef, address_space(0)))也是没有问题的。
  # define __safe __attribute__((safe)) 
  这个定义在sparse里也有,内核代码是在2.6.6-rc1版本变到2.6.6-rc2的时候被Linus加入的,为什么Linus要加入这个定义呢?原因是这样的:
  有人发现在代码编译的时候,编译器对变量的检查有些苛刻,导致代码在编译的时候老是出问题。比如说这样一个例子:  int test( struct a * a, struct b * b, struct c * c ) { return a->a + b->b + c->c; }  这个编译的时候会有问题,因为没有检查参数是否为空,就直接进行调用。但是呢,在内核里,有好多函数,当它们被调用的时候,这些个参数必定不为空,所以根本用不着去对这些个参数进行非空的检查,所以呢,就增加了一个__safe的属性,如果修改一下: int test( struct a * __safe a, struct b * __safe b, struct c * __safe c ) { return a->a + b->b + c->c; }     也就是告诉编译器变量都是安全属性的,这样编译就没有问题了。不过我查找了linux2.6.34,arm的代码,里面已经没有使用这个宏定义变量的例子了,不知道是不是编译器现在已经支持这种特殊的情况了,所以就不用再加这样的代码了。
  # define __force __attribute__((force))
  表示所定义的变量类型是可以做强制类型转换的,在进行Sparse分析的时候,使其不用产生告警信息。
  # define __nocast __attribute__((nocast))
  这里表示这个变量的参数类型与实际参数类型一定得对得上才行,要不就在Sparse的时候生产告警信息。
  # define __iomem __attribute__((noderef, address_space(2)))
  这个定义与__user, __user是一样的,只不过这里的变量地址是需要在设备地址映射空间的。
  # define __acquires(x) __attribute__((context(x,0,1)))
  # define __releases(x) __attribute__((context(x,1,0)))
  这是一对相互关联的函数定义,第一句表示参数x在执行之前,引用计数必须为0,执行后,引用计数必须为1,第二句则正好相反,这个宏定义用在修饰函数定义的变量。
  # define __acquire(x) __context__(x,1)
  # define __release(x) __context__(x,-1)
  这是一对相互关联的函数定义,第一句表示要增加变量x的计数,增加量为1,第二句则正好相反,这个宏定义用在函数执行的过程中。
  以上四句如果在代码中出现了不平衡的状况,那么在Sparse的检测中就会报警。当然,Sparse的检测只是一个手段,而且是静态检查代码的手段,所以它的帮助有限,有可能把正确的认为是错误的而发出告警。要是对以上四句的意思还是不太了解的话,请在源代码里搜一下相关符号的用法就能知道了。这两组,在本质上没什么区别,只是在使用的位置上有所区别罢了。
  # define __cond_lock(x,c) ((c) ? ({ __acquire(x); 1; }) : 0)
  这句话的意思就是条件锁。当c这个值不为0时,则让计数值加1,并返回值为1。不过这里我有一个疑问,就是在这里,有一个__cond_lock定 义,但没有定义相应的__cond_unlock,那么在变量的释放上,就没办法做到一致。而且我查了一下关于spin_trylock()这个函数的定 义,它就用了__cond_lock,而且里面又用了_spin_trylock函数,在_spin_trylock函数里,再经过几次调用,就会使用到 __acquire函数,这样的话,相当于一个操作,就进行了两次计算,会导致Sparse的检测出现告警信息,经过我写代码进行实验,验证了我的判断, 确实是会出现告警信息,如果我写两遍unlock指令,就没有告警信息了,但这是与程序的运行是不一致的。
  # define __percpu       __attribute__((noderef, address_space(3)))
  该定义用于多处理器(SMP)的情况。以下是来自http://lkml.org/lkml/2010/1/25/107的解释。
  __percpu annotation teaches sparse that percpu variables live in a separate address space and can't be accessed directly without going through percpu accessors.  This allows detection of most percpu access mistakes involving both static and dyanmic percpu variables.
  到目前为止没有发现有解释说这个(3)空间是什么空间的。
  extern void __chk_user_ptr(const volatile void __user *);
  extern void __chk_io_ptr(const volatile void __iomem *);
  这两句比较有意思。这里只是定义了函数,但是代码里没有函数的实现。这样做的目的,就是在进行Sparse的时候,让Sparse给代码做必要的参数类型检查,在实际的编译过程中,并不需要这两个函数的实现。
  #ifdef __KERNEL__
  这个符号用于选择使用头文件的哪一部分。由于libc包含了这些头文件,应用程序最终也会包含内核头文件,但应用程序不需要内核原型。于是就用__KERNEL__符号将那些额外的去掉。将内核符号和宏开放给用户空间的程序会造成那个程序的名字空间污染。该宏可以由打算使用要包含的头文件中该条件编译包含的代码定义的宏的模块文件定义,亦可编译时通过-D选项来定义( -D__KERNEL__ )并传递给编译器,当然亦可在Makefile中完成该动作。
  #ifdef __GNUC__
  #include 
  #endif
  1.__GNUC__ 是gcc编译器编译代码时预定义的一个宏。需要针对gcc编写代码时, 可以使用该宏进行条件编译。
  2.__GNUC__ 的值表示gcc的版本。需要针对gcc特定版本编写代码时,也可以使用该宏进行条件编译。
  3.__GNUC__ 的类型是"int". 该宏被扩展后, 得到的是整数字面值。
  此处如果定义了该宏则继续把compiler-gcc.h包含进来,该头文件定义了gcc所有版本都支持的一般定义。
  #define notrace__attribute__((no_instrument_function))
  宏notrace的定义,这个宏用于修饰函数,说明该函数不被跟踪。这里所说的跟踪是gcc一个很重要的特性,只要在编译时打开相关的跟踪选择,编译器会加入一些特性,使得程序在执行完后,可以通过工具(如Graphviz)来查看函数的调用过程。而对于内核来说,内部采用了ftrace机制,而不采用trace的特性。
  /* Intel compiler defines __GNUC__. So we will overwrite implementations
  * coming from above header files here 
  */
  #ifdef __INTEL_COMPILER
  # include 
  #endif
  此处的注释解释的已然相当明了,不再多说。真希望内核中多些注释啊。不过免费给你的代码,还能要求什么呢?
  上面包含的文件compiler-gcc.h和compiler-intel.h里面定义了编译器相关的内容,下面开始定义通用的一些编译属性。
  这里首先定义了结构体ftrace_branch_data,其用于记录ftrace branch的trace记录。什么是ftrace呢?(这里内容比较多哦...)
  ftrace 是当前 Linux 内核中一种新的 trace 工具。ftrace 的作用是帮助开发人员了解 Linux 内核的运行时行为,以便进行故障调试或性能分析。最早 ftrace 是一个 function tracer,仅能够记录内核的函数调用流程。如今ftrace 已经成为一个 framework,采用 plugin 的方式支持开发人员添加更多种类的 trace 功能。Ftrace 由 RedHat 的 Steve Rostedt 负责维护。branch tracer就是ftrace其中一个功能,用于跟踪内核程序中的 likely/unlikely 分支预测命中率情况。 Branch tracer 能够记录这些分支语句有多少次预测成功。从而为优化程序提供线索。
  ftrace 在内核态工作,用户通过 debugfs 接口来控制和使用 ftrace 。从 2.6.30 开始,ftrace 支持两大类 tracer:传统 tracer 和 Non-Tracer Tracer 。下面分别简单介绍他们的使用。
  传统 Tracer 的使用
  使用传统的 ftrace 需要如下几个步骤: 选择一种 tracer 
  使能 ftrace 
  执行需要 trace 的应用程序,比如需要跟踪 ls,就执行 ls 
  关闭 ftrace 
  查看 trace 文件 
  用户通过读写 debugfs 文件系统中的控制文件完成上述步骤。使用 debugfs,首先要挂载她。命令如下: 此时您将在 /debug 目录下看到 tracing 目录。 Ftrace 的控制接口就是该目录下的文件。
  选择 tracer 的控制文件叫作 current_tracer 。选择 tracer 就是将 tracer 的名字写入这个文件,比如,用户打算使用 function tracer,可输入如下命令: 文件 tracing_enabled 控制 ftrace 的开始和结束。 上面的命令使能 ftrace 。同样,将 0 写入 tracing_enable 文件便可以停止 ftrace 。
  ftrace 的输出信息主要保存在 3 个文件中。 Trace,该文件保存 ftrace 的输出信息,其内容可以直接阅读。 
  latency_trace,保存与 trace 相同的信息,不过组织方式略有不同。主要为了用户能方便地分析系统中有关延迟的信息。 
  trace_pipe 是一个管道文件,主要为了方便应用程序读取 trace 内容。算是扩展接口吧。
  Non-Tracer Tracer 的使用
  从 2.6.30 开始,ftrace 还支持几种 Non-tracer tracer,所谓 Non-tracer tracer 主要包括以下几种: 和传统的 tracer 不同,Non-Tracer Tracer 并不对每个内核函数进行跟踪,而是一种类似逻辑分析仪的模式,即对系统进行采样,但似乎也不完全如此。无论怎样,这些 tracer 的使用方法和传统tracer 的使用稍有不同。
  下面还是来介绍下此处代码涉及的branch tracer。
  Branch tracer 比较特殊,她有两种模式,即是传统 tracer,又实现了 profiling tracer 模式。
  作为传统 tracer 。其输出文件为 trace,格式如下: 在 FUNCTION 列中,显示了 4 类信息:
  函数名,文件和行号,用中括号引起来的部分,显示了分支的信息,假如该字符串为 ok,表明 likely/unlikely 返回为真,否则字符串为 MISS 。举例来说,在文件 file.h 的第 29 行,函数 fput_light 中,有一个 likely 分支在运行时解析为真。我们看看 file.h 的第 29 行:  Trace 结果告诉我们,在 688 秒的时候,第 29 行代码被执行,且预测结果为 ok,即 unlikely 成功。
  Branch tracer 作为 profiling tracer 时,其输出文件为 profile_annotated_branch,其中记录了 likely/unlikely 语句完整的统计结果。  记录表明,unlikely 在这里有 46 次为假,命中率为 100% 。假如为真的次数更多,则说明这里应该改成 likely 。
  ftrace 的实现
  研究 tracer 的实现是非常有乐趣的。理解 ftrace 的实现能够启发我们在自己的系统中设计更好的 trace 功能。
  ftrace 的整体构架
  Ftrace 的整体构架:
  图 1. ftrace 组成
  
  Ftrace 有两大组成部分,一是 framework,另外就是一系列的 tracer 。每个 tracer 完成不同的功能,它们统一由 framework 管理。 ftrace 的 trace 信息保存在 ring buffer 中,由 framework 负责管理。 Framework 利用 debugfs 系统在 /debugfs 下建立 tracing 目录,并提供了一系列的控制文件。
  branch tracer 的实现
  内核代码中常使用 likely 和 unlikely 提高编译器生成的代码质量。 Gcc 可以通过合理安排汇编代码最大限度的利用处理器的流水线。合理的预测是 likely 能够提高性能的关键,ftrace 为此定义了 branch tracer,跟踪程序中 likely 预测的正确率。为了实现 branch tracer,重新定义了 likely 和 unlikely 。也就是接下来代码做的事情。
  #define likely(x) __builtin_expect(!!(x), 1)
  #define unlikely(x) __builtin_expect(!!(x), 0)
  这两句是一对对应关系。__builtin_expect(expr, c)这个函数是新版gcc支持的,它是用来作代码优化的,用来告诉编译器,expr的期,非常有可能是c,这样在gcc生成对应的汇编代码的时候,会把相应的可能执行的代码都放在一起,这样能少执行代码的跳转。为什么这样能提高CPU的执行效率呢?因为CPU在执行的时候,都是有预先取指令的机制的,把将要执行的指令取出一部分出来准备执行。CPU不知道程序的逻辑,所以都是从可程序程序里挨着取的,如果这个时候,能不做跳转,则CPU预先取出的指令都可以接着使用,反之,则预先取出来的指令都是没有用的。还有个问题是需要注意的,在__builtin_expect的定义中,以前的版本是没有!!这个符号的,这个符号的作用其实就是负负得正,为什么要这样做呢?就是为了保证非零的x的值,后来都为1,如果为零的0值,后来都为0,仅此而已。
  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的所有分支,并记录分支的跟踪信息。
  #ifndef barrier
  # define barrier() __memory_barrier()
  #endif
  这里表示如果没有定义barrier函数,则定义barrier()函数为__memory_barrier()。但在内核代码里,是会包含compiler-gcc.h这个文件的,所以在这个文件里,定义barrier()为__asm__ __volatile__("": : :"memory")。barrier翻译成中文就是屏障的意思,在这里,为什么要一个屏障呢?这是因为CPU在执行的过程中,为了优化指令,可能会对部分指令以它自己认为最优的方式进行执行,这个执行的顺序并不一定是按照程序在源码内写的顺序。编译器也有可能在生成二进制指令的时候,也进行一些优化。这样就有可能在多CPU,多线程或是互斥锁的执行中遇到问题。那么这个内存屏障可以看作是一条线,内存屏障用在这里,就是为了保证屏障以上的操作,不会影响到屏障以下的操作。然后再看看这个屏障怎么实现的。__asm__表示后面的东西都是汇编指令,当然,这是一种在C语言中嵌入汇编的方法,语法有其特殊性,我在这里只讲跟这条指令有关的。__volatile__表示不对此处的汇编指令做优化,这样就会保证这里代码的正确性。""表示这里是个空指令,那么既然是空指令,则所对应的指令所需要的输入与输出都没有。在gcc中规定,如果以这种方式嵌入汇编,如果输出没有,则需要两个冒号来代替输出操作数的位置,所以需要加两个::,这时的指令就为"" : :。然后再加上为分隔输入而加入的冒号,再加上空的输入,即为"" : : :。后面的memory是gcc中的一个特殊的语法,加上它,gcc编译器则会产生一个动作,这个动作使gcc不保留在寄存器内内存的值,并且对相应的内存不会做存储与加载的优化处理,这个动作不产生额外的代码,这个行为是由gcc编译器来保证完成的。如果对这部分有更大的兴趣,可以考察gcc的帮助文档与内核中一篇名为memory-barriers.txt的文章。
  ################################################## ###############################
  接下来好多定义都没有实现,可以看一看注释就知道了,所以这里就不多说了。唉,不过再插一句,__deprecated属性的实现是为deprecated。
  #define noinline_for_stack noinline
  #ifndef __always_inline
  #define __always_inline inline
  #endif
  这里noinline与inline属性是两个对立的属性,从词面的意思就非常好理解了。
  #ifndef __cold
  #define __cold
  #endif
  从注释中就可以看出来,如果一个函数的属性为__cold,那么编译器就会认为这个函数几乎是不可能被调用的,在进行代码优化的时候,就会考虑到这一点。不过我没有看到在gcc里支持这个属性的说明。
  #ifndef __section
  # define __section(S) __attribute__ ((__section__(#S)))
  #endif
  这个比较容易理解了,用来修饰一个函数是放在哪个区域里的,不使用编译器默认的方式。这个区域的名字由定义者自己取,格式就是__section__加上用户输入的参数。
  #define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
  这个函数的定义很有意思,它就是访问这个x参数所对应的东西一次,它是这样做的:先取得这个x的地址,然后把这个地址进行变换,转换成一个指向这个地址类型的指针,然后再取得这个指针所指向的内容。这样就达到了访问一次的目的,哈哈。
分享到:
评论

相关推荐

    linux/cyclades.h 头文件

    移动文件到 /usr/include/linux 目录下即可

    sogou_toolchain.tar.gz

    arm-openwrt-linux-g++ -c record.c -I./include -o ./obj/record.o In file included from ./include/alsa/asoundlib.h:32:0, from record.c:13: /home/rootroot/sogou/toolchain/include/sys/poll.h:1:2: warning...

    opengl头文件khrplatform.h,glext.h,glcorearb.h,glxext.h,wglext.h

    2018年为止opengl官方提供的拓展头文件。 <GL/glext.h> - OpenGL 1.2 and above compatibility profile and extension interfaces. <GL/glcorearb.h> - OpenGL core profile and ARB extension interfaces, as ...

    mybatis-3-config/mapper.dtd 解决mybatis头文件报错

    解决mybatis头文件报错 下载好压缩包 解压将文件放到本地文件夹 例如 D盘的哪个文件夹 D:\mybatis\ ;然后打开eclipse ->Window->prefenrence->XML->XML Catalog->User Specifiled Entreis->Add->Location(此处是你...

    sched的头文件内容

    /* * include/linux/schedh * */ #ifndef _LINUX_SCHED_H #define _LINUX_SCHED_H #include <asm/param.h> /* for HZ */ extern unsigned long event;...#include <linux/config.h> ...#include <linux/sem.h>

    libjpeg.so.62.0.0.rar

    7、将/TARGETDIR/include/中(jconfig.h, jerror.h, jmorecfg.h, jpeglib.h)四个头文件拷贝到:/usr/local/arm/3.4.1/arm-linux/include中。 将/TARGETDIR/lib中(libjpeg.la, libjpeg.so, libjpeg.so.62, ...

    在ubuntu虚拟机下关于缺少头文件<gnu/stubs.h>的解决办法

    在ubuntu虚拟机下关于缺少头文件&lt;gnu/stubs.h&gt;的解决办法

    头文件免费下载

    // stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, but // are changed infrequently // #if !defined(AFX_STDAFX_H__C4B5DA9B_21...

    features.h标准c/c++头文件下载

    features.h 标准c/c++头文件下载

    LINUX下的arm2410开发板ADC 驱动程序

    #include&lt;linux/config.h&gt; #include&lt;linux/module.h&gt; #include&lt;linux/kernel.h&gt; #include&lt;linux/init.h&gt; #include&lt;asm/hardware.h&gt; //硬件头文件 #include&lt;asm/uaccess.h&gt; //用户和内核数据复制头文件 #define ...

    #include "stdio.h" /* 输入输出函数的头文件 */

    发生的爱的 安达市大大 免费的桑拿房,上的发生的,能否,是发生了罚款是否 使得房价司法是否 是否适当方式的是按时打发似的发射的共同回顾房间号 好风光好多人 人啊打发打发的是官方的风格的

    AVR所有头文件

    包含所有的AVR头文件,如: #include &lt;inttypes.h&gt; #include &lt;avr/io.h&gt; #include &lt;avr/pgmspace.h&gt; #include &lt;avr/wdt.h&gt;

    arduino的wire.h头文件压缩包

    arduino的wire.h头文件压缩包arduino的wire.h头文件压缩包arduino的wire.h头文件压缩包arduino的wire.h头文件压缩包arduino的wire.h头文件压缩包arduino的wire.h头文件压缩包arduino的wire.h头文件压缩包arduino的...

    intsafe.h头文件 开发资源下载 http://download.csdn.net/user/jiaao

    intsafe.h头文件 开发资源下载 http://download.csdn.net/user/jiaao

    fatal error C1083: 无法打开包括文件:“stdint.h”: No such file or directory

    【原因】stdint.h是c99标准的头文件,vc不支持,所以肯定会提示“No such file or directory”的。 【解决方案】 1. 去http://download.csdn.net/download/liubing8609/10046490下载“inttypes.h+stdint.h”压缩文件...

    linux内核list.h完整源代码

    本文档是从GiHup上linux/include/linux/list.h复制过来的,有完整版的list.h,但其中含包含Linux的其它头文件的函数,但基本不影响阅读

    linux sdl 库和头文件

    linux下sdl库文件和头文件-------------------------------

    apue.h 《UNIX环境高级编程》 头文件

    所以因该将它放入系统头文件中(Linux下是 /usr/include),这样gcc编译器就可以找到它了。 1. 超级用户权限登入 #cd /usr/include 2. 将apue.h和error.c两个文件copy到该目录下。(apue.h位于 your_apue_path/...

    C语言头文件 TYPEINFO.H

    C语言头文件 TYPEINFO.HC语言头文件 TYPEINFO.HC语言头文件 TYPEINFO.HC语言头文件 TYPEINFO.HC语言头文件 TYPEINFO.HC语言头文件 TYPEINFO.HC语言头文件 TYPEINFO.HC语言头文件 TYPEINFO.HC语言头文件 TYPEINFO.HC...

    sys/**.h sys文件夹里面的头文件

    socket.h uio.h types.h cdefs.h fcntl.h file.h locking.h param.h .utime.h.. GNU 里面的C library,,对应的头文件

Global site tag (gtag.js) - Google Analytics