Linux驱动开发—— IS_ENABLED

 

在閱讀Linux內核代碼的時候,會經常遇到下面的幾個宏函數:

IS_ENABLED  這個宏最爲常見

IS_BUILTIN

IS_MODULE

IS_REACHABLE

這幾個宏函數是在文件include/linux/kconfig.h中定義的,如下:

/*
 * Helper macros to use CONFIG_ options in C/CPP expressions. Note that
 * these only work with boolean and tristate options.
 */

/*
 * Getting something that works in C and CPP for an arg that may or may
 * not be defined is tricky.  Here, if we have "#define CONFIG_BOOGER 1"
 * we match on the placeholder define, insert the "0," for arg1 and generate
 * the triplet (0, 1, 0).  Then the last step cherry picks the 2nd arg (a one).
 * When CONFIG_BOOGER is not defined, we generate a (... 1, 0) pair, and when
 * the last step cherry picks the 2nd arg, we get a zero.
 */
#define __ARG_PLACEHOLDER_1 0,
#define config_enabled(cfg) _config_enabled(cfg)
#define _config_enabled(value) __config_enabled(__ARG_PLACEHOLDER_##value)
#define __config_enabled(arg1_or_junk) ___config_enabled(arg1_or_junk 1, 0)
#define ___config_enabled(__ignored, val, ...) val

/*
 * IS_ENABLED(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'y' or 'm',
 * 0 otherwise.
 *
 */
#define IS_ENABLED(option) \
    (config_enabled(option) || config_enabled(option##_MODULE))

/*
 * IS_BUILTIN(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'y', 0
 * otherwise. For boolean options, this is equivalent to
 * IS_ENABLED(CONFIG_FOO).
 */
#define IS_BUILTIN(option) config_enabled(option)

/*
 * IS_MODULE(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'm', 0
 * otherwise.
 */
#define IS_MODULE(option) config_enabled(option##_MODULE)

/*
 * IS_REACHABLE(CONFIG_FOO) evaluates to 1 if the currently compiled
 * code can call a function defined in code compiled based on CONFIG_FOO.
 * This is similar to IS_ENABLED(), but returns false when invoked from
 * built-in code when CONFIG_FOO is set to 'm'.
 */
#define IS_REACHABLE(option) (config_enabled(option) || \
         (config_enabled(option##_MODULE) && config_enabled(MODULE)))

 

首先需要有一些基礎知識.

我們知道,在進行內核配置的時候,內核配置若是boolean類型,那麼對應的配置有y和n兩種狀態,如果是tristate,那麼對應的配置有y,n和m三種狀態.

然後,在編譯內核的時候會生成include/generated/autoconf.h

如果某個配置被設置爲了y,那麼在.config文件中:

 CONFIG_SND_DUMMY=y

在autoconf.h中會看到:

 #define CONFIG_SND_DUMMY 1 

如果被設置爲了m,表示這個部分代碼會編譯成內核模塊,那麼在.config中會看到:

 CONFIG_SND_DUMMY=m 

在autoconf.h中會看到:

 #define CONFIG_SND_DUMMY_MODULE 1 

可以看到,在宏的末尾多了一個_MODULE的後綴.

如果被設置爲了n,那麼在.config中會看到:

 # CONFIG_SND_DUMMY is not set 

而在autoconf.h中看不到這個宏的任何定義.

對於靜態編譯到kernel中的文件, 在編譯的時候會宏MODULE不會被定義

而對於編譯到模塊中的文件,在編譯的時候,編譯系統會定義MODULE宏.

 

下面解釋一下這幾個宏函數的作用

  • IS_ENABLED

如果option或者option_MODULE所表示的宏定義在當前上下文被設置爲了1, 那麼這個宏函數會返回1;如果沒有定義或者被定義爲了0, 那麼就返回0.

無論CONFIG_SND_DUMMY被設置爲了y還是m,IS_ENABLED(CONFIG_SND_DUMMY)都會返回1.

  • IS_BUILTIN

如果option所表示的宏定義在當前上下文被設置爲了1, 那麼這個宏函數會返回1;如果沒有定義或者被定義爲了0, 那麼就返回0.

只有CONFIG_SND_DUMMY被設置爲了y,IS_BUILTIN(CONFIG_SND_DUMMY)才會返回1.

  • IS_MODULE

如果option_MODULE所表示的宏定義在當前上下文被設置爲了1, 那麼這個宏函數會返回1;如果沒有定義或者被定義爲了0, 那麼就返回0.

只有CONFIG_SND_DUMMY被設置爲了m,IS_MODULE(CONFIG_SND_DUMMY)才會返回1.

  • IS_REACHABLE

這個宏函數跟IS_ENABLED的類似, 不同之處是,如果CONFIG_SND_DUMMY被設置爲了m,那麼IS_REACHABLE(CONFIG_SND_DUMMY)在靜態編譯到kernel中的文件中被預處理的時候會返回0,而在編譯到模塊的文件中預處理時會返回0.原因是MODULE宏只有在編譯模塊代碼期間纔會被定義.

 

下面我們將config_enabled(CONFIG_SND_DUMMY)展開:

如果CONFIG_SND_DUMMYautoconf.h中被設置爲了1, 那麼展開如下:

___config_enabled(__ARG_PLACEHOLDER_1 1, 0)

再進一步展開

___config_enabled(0, 1, 0) 

最後展開如下:

  1 

 

如果CONFIG_SND_DUMMY在autoconf.h中沒有被設置, 那麼展開如下:

 ___config_enabled(__ARG_PLACEHOLDER_CONFIG_SND_DUMMY 1, 0) 

進一步展開如下:

  0 

 

下面用一個簡單的測試程序試試:

a.c

 1 #define __ARG_PLACEHOLDER_1 0,
 2 #define config_enabled(cfg) _config_enabled(cfg)
 3 #define _config_enabled(value) __config_enabled(__ARG_PLACEHOLDER_##value)
 4 #define __config_enabled(arg1_or_junk) ___config_enabled(arg1_or_junk 1, 0)
 5 #define ___config_enabled(__ignored, val, ...) val
 6 
 7 #define ONE 2
 8 #define TWO 1
 9 #define THREE 0
10 #define FOUR
11 
12 int main(int argc, char *argv[])
13 {
14 
15     int a;
16 
17     if (config_enabled(ONE)) {
18          a = 1;
19     }
20 
21     if (config_enabled(TWO)) {
22          a = 1;
23     }
24 
25     if (config_enabled(THREE)) {
26          a = 1;
27     }
28 
29     if (config_enabled(FOUR)) {
30          a = 1;
31     }
32 
33     if (config_enabled(FIVE)) {
34          a = 1;
35     }
36 
37     return 0;
38 }

 

 然後使用gcc的預編譯命令 gcc -E a.c -o a.i 編譯出文件a.i如下:

 1 int main(int argc, char *argv[])
 2 {
 3 
 4  int a;
 5 
 6  if (0) {
 7    a = 1;
 8  }
 9 
10  if (1) {
11    a = 1;
12  }
13 
14  if (0) {
15    a = 1;
16  }
17 
18  if (0) {
19    a = 1;
20  }
21 
22  if (0) {
23    a = 1;
24  }
25 
26  return 0;
27 }

 

 

完.

posted @ 2016-10-14 14:45  摩斯电码  阅读(4160)  评论(0编辑  收藏  举报