免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 17565 | 回复: 9

测试test_bit(int a, long *b)宏的返回值 [复制链接]

论坛徽章:
0
发表于 2011-03-17 18:05 |显示全部楼层
最近在看2.4内核网络协议栈的代码时发现很多地方会根据test_bit()宏的返回值的不同来进行不同的操作,形如:
return test_bit(IPS_CONFIRMED_BIT, &ct->status);
为了搞清楚这个函数到底在什么情况下会返回0,写了个简单的测试程序,
测试后发现如果被测试位的值为0,就返回0;如果被测试位的值为1,就返回非0;不知道我的测试结果对不对?

论坛徽章:
22
丑牛
日期:2014-08-15 14:32:0015-16赛季CBA联赛之同曦
日期:2017-12-14 15:28:14黑曼巴
日期:2017-08-10 08:14:342017金鸡报晓
日期:2017-02-08 10:39:42黑曼巴
日期:2016-11-15 15:48:38CU十四周年纪念徽章
日期:2016-11-09 13:19:1015-16赛季CBA联赛之同曦
日期:2016-04-08 18:00:03平安夜徽章
日期:2015-12-26 00:06:30程序设计版块每日发帖之星
日期:2015-12-03 06:20:002015七夕节徽章
日期:2015-08-21 11:06:17IT运维版块每日发帖之星
日期:2015-08-09 06:20:002015亚冠之吉达阿赫利
日期:2015-07-03 08:39:42
发表于 2011-03-21 10:15 |显示全部楼层
你去看看代码实现也行
不过你都亲手测试了还不相信啊。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
发表于 2011-03-21 11:11 |显示全部楼层
最近在看2.4内核网络协议栈的代码时发现很多地方会根据test_bit()宏的返回值的不同来进行不同的操作,形如 ...
Anzyfly 发表于 2011-03-17 18:05
没有具体看代码的定义,但是函数的说明当中,你是对的。
  1. 285 /**
  2. 286  * test_bit - Determine whether a bit is set
  3. 287  * @nr: bit number to test
  4. 288  * @addr: Address to start counting from
  5. 289  */
复制代码

论坛徽章:
0
发表于 2011-03-21 11:23 |显示全部楼层
最近在看2.4内核网络协议栈的代码时发现很多地方会根据test_bit()宏的返回值的不同来进行不同的操作,形如 ...
Anzyfly 发表于 2011-03-17 18:05



    看字面含义。。。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
发表于 2011-03-21 13:31 |显示全部楼层
最近在看2.4内核网络协议栈的代码时发现很多地方会根据test_bit()宏的返回值的不同来进行不同的操作,形如 ...
Anzyfly 发表于 2011-03-17 18:05
忍不住,又看了一下内核代码。顺便分析了一下,希望对你更底层的了解有用。
  1. #if 0 /* Fool kernel-doc since it doesn't do macros yet */
  2. /**
  3. * test_bit - Determine whether a bit is set
  4. * @nr: bit number to test
  5. * @addr: Address to start counting from
  6. */
  7. static int test_bit(int nr, const volatile void * addr);
  8. #endif

  9. static __always_inline int constant_test_bit(int nr, const volatile unsigned long *addr)
  10. {
  11.         return ((1UL << (nr & 31)) & (addr[nr >> 5])) != 0;
  12. }
复制代码
对于编译时的常数使用上面的函数进行判断
假设我们要测试的数为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。其他的情况照样子可以这样子验证。
  1. static inline int variable_test_bit(int nr, const volatile unsigned long * addr)
  2. {
  3.         int oldbit;

  4.         __asm__ __volatile__(
  5.                 "btl %2,%1\n\tsbbl %0,%0"
  6.                 :"=r" (oldbit)
  7.                 :"m" (ADDR),"Ir" (nr));
  8.         return oldbit;
  9. }
复制代码
对于编译时的非常数,使用上面的函数进行判断。上面使用了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
  1. #define test_bit(nr,addr) \
  2. (__builtin_constant_p(nr) ? \
  3. constant_test_bit((nr),(addr)) : \
  4. 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.

论坛徽章:
0
发表于 2011-03-21 21:32 |显示全部楼层
return ((1UL << (nr & 31)) & (addr[nr >> 5])) != 0; 这个语句的意思你是怎么分析出来的?
能否再帮我指点一下,我搞不清楚((1UL << (nr & 31)) 和(addr[nr >> 5]))到底表达了一个什么
意思,没有真正看懂它的实现过程。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
发表于 2011-03-21 22:53 |显示全部楼层
return ((1UL > 5])) != 0; 这个语句的意思你是怎么分析出来的?
能否再帮我指点一下,我搞不清楚((1UL >  ...
Anzyfly 发表于 2011-03-21 21:32



    ((1UL << (nr & 31)) & (addr[nr >> 5])) != 0

对于0~31之间的数字,nr & 31就是nr,然后 1UL << nr把第nr位置位,其它为零。

后面那个就是数组和指针的混用了。对于0~31的数字,nr >> 5总是为0的,也就是数组的第一个元素,对应就是指针指向的元素,(addr)。

论坛徽章:
0
发表于 2011-03-22 10:04 |显示全部楼层
在/asm-i386/bitops.h中的定义如下:
#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 __inline__ int constant_test_bit(int nr, const volatile void * addr)
{
        return ((1UL << (nr & 31)) & (((const volatile unsigned int *) addr)[nr >> 5])) != 0;
}

在.../asm-generic/bitops.h中的定义如下:
extern __inline__ int test_bit(int nr, long * addr)
{
        int        mask;

        addr += nr >> 5;
        mask = 1 << (nr & 0x1f);
        return ((mask & *addr) != 0);
}

这两定义其实都是正如你分析的那样,都是可以释的通的,现在我终于知道这个测试置位函数的实现过程了,
只是感概内核中的函数为什么都要这么写?简单的一个函数式 return ((1<<nr) & addr[0])就可以表达的很清楚的过程,总是搞的这么复杂,是为了可移植性?还是非要把人整晕?还是我考虑的太简单了?

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
发表于 2011-03-22 19:01 |显示全部楼层
在/asm-i386/bitops.h中的定义如下:
#if 0 /* Fool kernel-doc since it doesn't do macros yet */
/**
...
Anzyfly 发表于 2011-03-22 10:04
你说的那个可能考虑了所要测试的对象是结构或者数组的原因吧。

论坛徽章:
0
发表于 2012-09-11 09:33 |显示全部楼层
因为__alloc_bootmem_core函数中test_bit (j, bdata->node_bootmem_map)传递过去以后,nr=j,addr=bdata->node_bootmem_map,其中nr=j是要开始查找的页框号,位图保存在bdata->node_bootmem_map指向的地方,并且是32位的unsigned long型,因此 addr)[nr >> 5])也即是 addr)[nr/32]),是为了将查找地址移动到要查找的页位图大致位置(第几个unsigned long型的数开始)。(nr & 31)即是(nr %31)即是算出在某一个unsigned long内的要开始查找的精确位置,然后将1UL左移(nr & 31)即是要在一个unsigned long中要查找的精确位置。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP