- 论坛徽章:
- 1
|
最近在看2.4内核网络协议栈的代码时发现很多地方会根据test_bit()宏的返回值的不同来进行不同的操作,形如 ...
Anzyfly 发表于 2011-03-17 18:05 ![]() 忍不住,又看了一下内核代码。顺便分析了一下,希望对你更底层的了解有用。- #if 0 /* Fool kernel-doc since it doesn't do macros yet */
- /**
- * test_bit - Determine whether a bit is set
- * @nr: bit number to test
- * @addr: Address to start counting from
- */
- static int test_bit(int nr, const volatile void * addr);
- #endif
- static __always_inline int constant_test_bit(int nr, const volatile unsigned long *addr)
- {
- return ((1UL << (nr & 31)) & (addr[nr >> 5])) != 0;
- }
复制代码 对于编译时的常数使用上面的函数进行判断
假设我们要测试的数为test = 0x00000008,它的第三位是否为1(位数是从0数起),即nr = 3 = 0x00000003,addr就是我们测试的数的地址
addr = &test; 对于运算addr[nr >> 5] = addr[0] 也就是test的值
对于前面部分 1UL左移3位,其值就是0x00000008,它们&运算后,结果是0x00000008 != 0,所以return的结果返回是1
即0x00000008的第3位是1,其结果也确实是1。其他的情况照样子可以这样子验证。- static inline int variable_test_bit(int nr, const volatile unsigned long * addr)
- {
- int oldbit;
- __asm__ __volatile__(
- "btl %2,%1\n\tsbbl %0,%0"
- :"=r" (oldbit)
- :"m" (ADDR),"Ir" (nr));
- return oldbit;
- }
复制代码 对于编译时的非常数,使用上面的函数进行判断。上面使用了gcc的内联汇编。参数nr和addr的含义和上面相同,其实就两句,翻译成Intel格式如下:
BT nr, (addr) ;测试(addr)的第nr位,如果是1,则CF = 1
SUBB oldbit, oldbit ;带进位的减法,因为上面置CF = 1,所以结果是1
然后函数最后返回oldbit的值,也就是1了。
上面的汇编语言可以参看Intel Architecture Software Developer’s Manual- #define test_bit(nr,addr) \
- (__builtin_constant_p(nr) ? \
- constant_test_bit((nr),(addr)) : \
- variable_test_bit((nr),(addr)))
复制代码 最后,test_bit其实是一个宏,看nr的属性(是否是编译时常数调用不同的函数),gcc的内建函数__builtin_constant_p(nr),测试nr是否是编译时的常量,如果是则返回1,然后调用constant_test_bit函数,否则调用variable_test_bit函数。下面是引用gcc手册的关于__builtin_constant_p(EXP)内建函数的说明。详细内容可以参看gcc的info文档。
You can use the built-in function `__builtin_constant_p' to
determine if a value is known to be constant at compile-time and
hence that GCC can perform constant-folding on expressions
involving that value. The argument of the function is the value
to test. The function returns the integer 1 if the argument is
known to be a compile-time constant and 0 if it is not known to be
a compile-time constant. A return of 0 does not indicate that the
value is _not_ a constant, but merely that GCC cannot prove it is
a constant with the specified value of the `-O' option. |
|