Linux内核源码分析(1)——compiler.h分析

原文:http://www.jtben.com/document/17660


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

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

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


#ifndef __ASSEMBLY__

 

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

 

       接下来是__CHECKER__宏的判断,__CHECKER__宏在通过Sparse(Semantic Parser forC)工具对内核代码进行检查时会定义的。在使用makeC=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特性用来修饰一个变量的地址,该变量必须是非解除参考(nodereference)即地址是有效的,并且变量所在的地址空间必须为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__的判断,后续部分的内容是内核模块相关的宏定义,不用于用户进程的代码。

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是否需要使用branchtracer跟踪。branchtracer是ftrace的一种trace功能。主要用于跟踪likely预测的正确率。为了实现这个功能,branchtracer重新定义了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 内核代码有效的定义

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中断代码间的变量值的一致性。

---------------------------------------------

compiler-gcc.h分析

=============================================

 在第一节分析了compiler.h文件,定义了当编译器为GCC时包含文件compiler-gcc.h。该文件是gcc编译器通用的定义。现在开始解析该文件的compiler-gcc.h。

 在第一节分析了compiler.h文件,定义了当编译器为GCC时包含文件compiler-gcc.h。该文件是gcc编译器通用的定义。现在开始解析该文件的compiler-gcc.h。

  --------------------------------------------------------------------------------

/* Optimization barrier */
/* The "volatile" is due to gcc bugs */
#define barrier() __asm__ __volatile__("": : :"memory")

--------------------------------------------------------------------------------

    barrier函数在之前介绍过了,就是做内存栅栏,它的作用是让栅栏上下的代码隔离开,不进行优化。

--------------------------------------------------------------------------------

/*
 * This macro obfuscates arithmetic on a variable address so that gcc
 * shouldn't recognize the original var, and make assumptions about it.
 *
 * This is needed because the C standard makes it undefined to do
 * pointer arithmetic on "objects" outside their boundaries and the
 * gcc optimizers assume this is the case. In particular they
 * assume such arithmetic does not wrap.
 *
 * A miscompilation has been observed because of this on PPC.
 * To work around it we hide the relationship of the pointer and the object
 * using this macro.
 *
 * Versions of the ppc64 compiler before 4.1 had a bug where use of
 * RELOC_HIDE could trash r30. The bug can be worked around by changing
 * the inline assembly constraint from =g to =r, in this particular
 * case either is valid.
 */
#define RELOC_HIDE(ptr, off)     \
  ({ unsigned long __ptr;     \
    __asm__ ("" : "=r"(__ptr) : "0"(ptr));  \
    (typeof(ptr)) (__ptr + (off)); })

--------------------------------------------------------------------------------

    该宏利用一条汇编语句,隐藏传入指针的类型,计算偏移后的地址。

--------------------------------------------------------------------------------

/* &a[0] degrades to a pointer: a different type from an array */
#define __must_be_array(a) \
  BUILD_BUG_ON_ZERO(__builtin_types_compatible_p(typeof(a), typeof(&a[0])))

--------------------------------------------------------------------------------

--------------------------------------------------------------------------------

/* Force a compilation error if condition is true, but also produce a
   result (of value 0 and type size_t), so the expression can be used
   e.g. in a structure initializer (or where-ever else comma expressions
   aren't permitted). */
#define BUILD_BUG_ON_ZERO(e) (sizeof(char[1 - 2 * !!(e)]) - 1)

--------------------------------------------------------------------------------

   该宏用于判断变量a是否是数组。该宏利用&a[0]的类型与a的类型不同的原则。__builtin_types_compatible_p(type1,type2)判断两个类型是否相同。BUILD_BUG_ON_ZERO宏在e为非0值时产生编译错误,为0时返回值0。

--------------------------------------------------------------------------------

/*
 * Force always-inline if the user requests it so via the .config,
 * or if gcc is too old:
 */
#if !defined(CONFIG_ARCH_SUPPORTS_OPTIMIZED_INLINING) || \
    !defined(CONFIG_OPTIMIZE_INLINING) || (__GNUC__ < 4)
# define inline  inline  __attribute__((always_inline))
# define __inline__ __inline__ __attribute__((always_inline))
# define __inline __inline __attribute__((always_inline))
#endif

--------------------------------------------------------------------------------

    这部分定义inline内联,用户可以通过.config文件配置。或者在GCC版本小于4.0时强制内联。

--------------------------------------------------------------------------------

#define __deprecated   __attribute__((deprecated))
#define __packed   __attribute__((packed))
#define __weak    __attribute__((weak))

--------------------------------------------------------------------------------

    __deprecated说明该函数被声明为废除,任何对该函数的调用,GCC都会产生一个告警。

    __packed用于修饰struct,union  ,enum类型定义,使得修饰后的类型占用最少的内存空间。对于类型为enum时,会选择采用最小内存的int指针。

    __weak修改符会将相应的声明的为弱符号,这经常会用在库函数里面,在库函数里面定义的弱函数可以在用户的应用的代码中的实现覆盖它。

--------------------------------------------------------------------------------

/*
 * it doesn't make sense on ARM (currently the only user of __naked) to trace
 * naked functions because then mcount is called without stack and frame pointer
 * being set up and there is no chance to restore the lr register to the value
 * before mcount was called.
 */
#define __naked    __attribute__((naked)) notrace

--------------------------------------------------------------------------------

    naked属性使编译器不产生函数的入口和出口,只编译函数体。naked把应用程序控制器交给了用户,怎样保存函数的上下文都由用户决定。

--------------------------------------------------------------------------------

#define __noreturn   __attribute__((noreturn))

--------------------------------------------------------------------------------

    noruten属性说明该函数不会返回。

--------------------------------------------------------------------------------

/*
 * From the GCC manual:
 *
 * Many functions have no effects except the return value and their
 * return value depends only on the parameters and/or global
 * variables.  Such a function can be subject to common subexpression
 * elimination and loop optimization just as an arithmetic operator
 * would be.
 * [...]
 */
#define __pure    __attribute__((pure))

--------------------------------------------------------------------------------

    pure属性修饰函数,说明该函数的输出完全依赖于函数的输入或全局的变量,这样的函数容易被通用的子表达式替换或优化成数学运算。

--------------------------------------------------------------------------------

#define __aligned(x)   __attribute__((aligned(x)))
#define __printf(a,b)   __attribute__((format(printf,a,b)))
#define  noinline   __attribute__((noinline))
#define __attribute_const__  __attribute__((__const__))
#define __maybe_unused   __attribute__((unused))

--------------------------------------------------------------------------------

    aligend为内存对齐修饰符

    format告诉编译器像检查printf,scanf等函数的输入参数格式一样检查该函数的输入参数。a表示第几个参数是字符串格式,b表示从第几个参数开始按字符串格式检查。

    noinline不进行内联的修饰符。

    __attribute_const__ 常量修饰符。

    __maybe_unused 修饰该函数可能不会被使用,gcc不会为这个函数产生警告。

--------------------------------------------------------------------------------

#define __gcc_header(x) #x
#define _gcc_header(x) __gcc_header(linux/compiler-gcc##x.h)
#define gcc_header(x) _gcc_header(x)
#include gcc_header(__GNUC__)

--------------------------------------------------------------------------------

    这个部分用于包含当前gcc版本相应的头文件compiler-gcc[x].h。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值