在分析自旋锁代码时,最终跟踪下来,会在 include/linux/compiler.h 中看到下面的代码:
其中含有 __context__ 这种属性。刚开始时认为这也是 gcc 支持的属性一种,其实不然。在应用程序里,模拟上面宏定义并包含 __context__ ,然后用 gcc 编译一下,会有错误提示。
实际上,该属性是提供给一种叫 sparse 的工具进行代码的静态检查的。关于 sparse 的介绍可参考:
在我们编译内核代码时,在 make 时使用 C=1 或者 C=2 选项时都会对 sparse 工具的调用。
sparse 进行代码静态检查并不会对代码进行编译,也不会进行相关宏的处理,它的工作只是检查而已。那么可能会有疑问,检查后,那最终还不是要用 gcc 来编译么?那这样一来不是还不能编译通过?这里需要注意到,像 # define __acquire(x) __context__(x,1) 这个宏,需要在 __CHECKER__ 这个宏定义下才能使用。那么搜遍整个内核代码,会发现没有 __CHECKER__ 的定义。但是回过头来查看 sparse 相关的源码时,会发现在对 sparse 初始化的函数里有这么一段(位于 lib.c 中):
再看 add_pre_buffer() 函数:
上面,就是将 "#define __CHECKER__ 1" 这一个字符串通过 vsnprintf(buffer, sizeof(buffer), fmt, args); 拷贝到缓冲区中。
一般的,对代码的静态检查工具,基本工作原理是将要检查的源代码读入缓冲区中在进行比较。所以,__CHECKER__ 这个定义是先在 sparse 的工具的缓冲区中定义好,这样,我们自然就能够使用带有 __context__ 属性的宏了。需要注意一点,带有 __context__ 的宏,只能在缓冲区中比较,在内核中最后仍然是使用没有 __context__ 这种属性的宏的,也就是说最终用到的是:
1
2
3
4
5
6
7
8
9
10
11
12
|
#ifdef __CHECKER__
...
# define __acquire(x) __context__(x,1)
# define __release(x) __context__(x,-1)
...
#else
# define __acquire(x) (void)0
# define __release(x) (void)0
...
#endif
|
其中含有 __context__ 这种属性。刚开始时认为这也是 gcc 支持的属性一种,其实不然。在应用程序里,模拟上面宏定义并包含 __context__ ,然后用 gcc 编译一下,会有错误提示。
实际上,该属性是提供给一种叫 sparse 的工具进行代码的静态检查的。关于 sparse 的介绍可参考:
引用
在我们编译内核代码时,在 make 时使用 C=1 或者 C=2 选项时都会对 sparse 工具的调用。
sparse 进行代码静态检查并不会对代码进行编译,也不会进行相关宏的处理,它的工作只是检查而已。那么可能会有疑问,检查后,那最终还不是要用 gcc 来编译么?那这样一来不是还不能编译通过?这里需要注意到,像 # define __acquire(x) __context__(x,1) 这个宏,需要在 __CHECKER__ 这个宏定义下才能使用。那么搜遍整个内核代码,会发现没有 __CHECKER__ 的定义。但是回过头来查看 sparse 相关的源码时,会发现在对 sparse 初始化的函数里有这么一段(位于 lib.c 中):
1
2
3
4
5
6
|
struct
symbol_list *sparse_initialize(
int
argc,
char
**argv,
struct
string_list **filelist)
{
....
add_pre_buffer(
"#define __CHECKER__ 1\n"
);
...
}
|
再看 add_pre_buffer() 函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
void
add_pre_buffer(
const
char
*fmt, ...)
{
va_list
args;
unsigned
int
size;
struct
token *begin, *end;
char
buffer[4096];
va_start
(args, fmt);
size = vsnprintf(buffer,
sizeof
(buffer), fmt, args);
va_end
(args);
begin = tokenize_buffer(buffer, size, &end);
if
(!pre_buffer_begin)
pre_buffer_begin = begin;
if
(pre_buffer_end)
pre_buffer_end->next = begin;
pre_buffer_end = end;
}
|
上面,就是将 "#define __CHECKER__ 1" 这一个字符串通过 vsnprintf(buffer, sizeof(buffer), fmt, args); 拷贝到缓冲区中。
一般的,对代码的静态检查工具,基本工作原理是将要检查的源代码读入缓冲区中在进行比较。所以,__CHECKER__ 这个定义是先在 sparse 的工具的缓冲区中定义好,这样,我们自然就能够使用带有 __context__ 属性的宏了。需要注意一点,带有 __context__ 的宏,只能在缓冲区中比较,在内核中最后仍然是使用没有 __context__ 这种属性的宏的,也就是说最终用到的是:
1
2
|
# define __acquire(x) (void)0
# define __release(x) (void)0
|