lerosua’s blog


DoWhile(0)

Posted in kernelnewbies 由 lerosua on the 八月 25, 2006

更详细参考Kernelnewbies

为啥内核里有这么多 do{ }while(0) 的宏啊?一开始我也好不明白。感觉不出用了会有什么效果。不过明白之后就知道它的好处了。好处就在于多语句的宏。

#define FOO(x)	print("arg is %sn",x);do_something(x);

在代码中使用:

if(2==blah)

	FOO(blah);

预编译展开后:

if(2==blah)

	print("arg is %sn",blah);

do_something(blah);

看到了吧,do_something函数已经脱离了if语句的控制了。这可不是我们想要的。使用do{}while(0);就万无一失了。

if (2== blah)

  do {

	printf("arg is %sn", blah);

	do_something(blah);

  } while (0);

当然你也可以使用下面这种形式:

#define exch(x,y) { int tmp; tmp=x; x=y; y=tmp; }

但是它在if-else语句中会出现问题。如:
if (x > y)

	exch(x,y);          // Branch 1

else

	do_something();     // Branch 2

展开后:

if (x > y) {                // Single-branch if-statement!!! 

	int tmp;            // The one and only branch consists

	tmp = x;            // of the block.

	x = y;

	y = tmp;v};                           // empty statementelse          

 // ERROR!!!   "parse error before else"do_something();

看到了吧,else成了语法错误了。使用do{}while(0)就不会有这个问题了。

if (x > y)

do {

	int tmp;

	tmp = x;

	x = y;

	y = tmp;

} while(0);

else

	do_something();

嗯,现在明白之后,自己的代码也记得用啊!

LikelyUnlikely

Posted in kernelnewbies 由 lerosua on the 八月 21, 2006

更好的解释请看Kernelnewbies

在内核中常会见到下面这样的代码。

bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx);

if (unlikely(!bvl)) {

  mempool_free(bio, bio_pool);

  bio = NULL;

  goto out;

}



这个likely()和unlikely()其实是宏定义。原始定义在
include/linux/compiler.h中,如下:


#define likely(x)       __builtin_expect(!!(x), 1)

#define unlikely(x)     __builtin_expect(!!(x), 0)

__builtin_expect是GCC内建的函数。下面引用linuxform中的一篇讲GCC扩展文章

的一部分(原我找到的也是引用的文章,此处给出链接),里面解释得很清楚。


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

* __builtin_expect(EXP, C)



内建函数 __builtin_expect 用于为编译器提供分支预测信息,其返回值是整数表达式 EXP
的值,C 的值必须是编译时常数。例如:

++++ include/linux/compiler.h

13: #define likely(x)       __builtin_expect((x),1)

14: #define unlikely(x)     __builtin_expect((x),0)

++++ kernel/sched.c

564:         if (unlikely(in_interrupt())) {

565:                 printk("Scheduling in interruptn");

566:                 BUG();

567:         }



这个内建函数的语义是 EXP 的预期值是 C,编译器可以根据这个信息适当地重排

语句块的顺序,使程序在预期的情况下有更高的执行效率。上面的例子表示处于中

断上下文是很少发生的,第 565-566 行的目标码可能会放在较远的位置,以保证

经常执行的目标码更紧凑。

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



总结,这个宏主要是优化的作用。如果英文好的话可以看文章一开始给出的

在kernelnewbies中的链接,那里有更详细的解说。


细心的人应该留意到有一点不同,likely()一开始偶给的定义为__builtin_expect(!!(x),1)。


后来的文章解说中成了__builtin_expect((x),1)。


这应该是后来的版本更改所至。就是新近版本的内核已经更改成__builtin_expect(!!(x),1).


加上个“!!”有什么好处哩。我想是因为使代码强壮与兼容的原因。因为不能保证x表达式的结果一定是1或0,有可能是非0数。加了“!!”,结果就一定是在1和0范围内了。

什么是asmlinkage

Posted in kernelnewbies 由 lerosua on the 八月 16, 2006

下面首先在kernelnewibes上的解释。

What is asmlinkage?  The asmlinkage tag is one other thing that we should observe aboutthis simple function. This is a #define for some gcc magic that tellsthe compiler that the function

should not expect to find any of itsarguments in registers (a common optimization),

but only on theCPU's stack. Recall our earlier assertion that system_call consumesits

first argument, the system call number, and allows up to four morearguments that are

passed along to the real system call. system_callachieves this feat simply by leaving its other

arguments (which werepassed to it in registers) on the stack. All system calls are markedwith

the asmlinkage tag, so they all look to the stack for arguments.Of course, in sys_ni_syscall's case,

this doesn't make any difference,because sys_ni_syscall doesn't take any arguments, but it's an

issuefor most other system calls. And, because you'll be seeingasmlinkage in front of many other

functions, I thought you shouldknow what it was about.

而我在 2.6.8的内核上的include/linux/linkage.h查到它的定义是:

 #ifndef asmlinkage

 #define asmlinkage CPP_ASMLINKAGE  #endif

进一步,CPP_ASMLINKAGE的定义为:

 #ifdef __cplusplus 

#define CPP_ASMLINKAGE extern "C"

#else

#define CPP_ASMLINKAGE

#endif

而在include/asm-i386/linkage.h里则有更明确的定义:

#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))

__attribute__是GCC的C语言扩展语法。regparm(0)表示不从寄存器中传递参数。

另外,如果是__attribute__((regparm(3))),那么调用函数的时候参数不是通过栈传递,而是直接放到寄存器里,被调用函数直接从寄存器取参数。

此句引自了海中一浪的博客文章

asmlinkage确保它定义的函数从栈中读取参数而不是在寄存器中。

啥是__init __exit宏

Posted in kernelnewbies 由 lerosua on the 八月 15, 2006

(学习内核笔记由此开始)

更详细内容请kernelnewibes
原始定义在include/linux/init.h

#define __init          __attribute__ ((__section__ (".init.text")))
#define __initdata      __attribute__ ((__section__ (".init.data")))

#define __exitdata      __attribute__ ((__section__(".exit.data")))

#define __exit_call     __attribute_used__ __attribute__ ((__section__ (".exitcall.exit")))

#ifdef MODULE

#define __exit          __attribute__ ((__section__(".exit.text")))

#else

#define __exit          __attribute_used__ __attribute__ ((__section__(".exit.text")))

#endif

__init和__exit标记函数,__initdata和__exitdata标记数据。

此宏定义可知标记后的函数与数据其实是放到了特定的(代码或数据)段中。

标记为初始化的函数表明公在初始化期间使用。
在模块装载之后,模块装载就

会将初始化函数扔掉。这样可以将该函数占用的内存释放出来。

__exit修饰词标记函数只在模块卸载时使用。如果模块被直接编进内核

或内核不允许卸载模块。被此标记的函数将被简单地丢弃。