首页 > 代码库 > Kernel散记——常见宏

Kernel散记——常见宏

 Kernel散记——常见宏

1,likely()和unlikey()
1.1,来自哪里?@kernel.h
#  define likely(x)     (__builtin_expect(!!(x), 1))
#  define unlikely(x)     (__builtin_expect(!!(x), 0))
1.2,作用?
代码优化。详细占说是告诉编译器,优化预取指。
1.3,细节?
细节也说不清,说个大概;
#define likely(x) __builtin_expect(!!(x), 1)也就是说明x==1是“经常发生的”或是“很可能发生的”。
使用likely ,执行if后面语句的可能性大些,编译器将if{}是的内容编译到前面, 使用unlikely ,执行else后面语句的可能性大些,编译器将else{}里的内容编译到前面。这样有利于cpu预取,提高预取指令的正确率,因而可提高效率。
1.4,举个例子
ret = copy_to_user(to, fifo->data + off, l);
     if (unlikely(ret))
          ret = DIV_ROUND_UP(ret + len - l, esize);
     else {
          ret = copy_to_user(to + l, fifo->data, len - l);
...
     }
也就是说ret=0的可能性很大,else的内容被编译到if之前,优化处理器取指

另外,使用!!(x)而不使用x是把x值转化为bool值。这个也是kernel常见的。


2,在Input 子系统中,对bits有几个常用的位操作宏
@include/linux/input.h struct input_dev
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
先看定义:
#define BITS_TO_LONGS(nr)     DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))

在32位系统中,long占用4个字节。

BITS_TOLONGS就是探测一下共总有多少个32bit。
比较ABS_CNT = 0x3f + 1 = 0x40 
(也就是说0x40 = 64 = 32 *2
两个long<32bit>型就可以装下)
所以BITS_TO_LONGS(ABS_CNT) = 2

@input.c
test_bit(ABS_MT_TRACKING_ID, dev->absbit)
test_bit 简单地说就是返回(dev->absbit)这个地址,对应的位(ABS_MT_TRACKING)的值。

@input.c
__set_bit(EV_SYN, dev->evbit);
定义:
/**
* __set_bit - Set a bit in memory
* @nr: the bit to set
* @addr: the address to start counting from
*
* Unlike set_bit(), this function is non-atomic and may be reordered.
* If it‘s called on the same region of memory simultaneously, the effect
* may be that only one operation succeeds.
*/
static inline void __set_bit(int nr, volatile unsigned long *addr)
{
     unsigned long mask = BIT_MASK(nr);
     unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);

     *p  |= mask;
}
简单地说,就是把(dev-evbit)这个内存开始的地址的第EV_SYN(0)位

综上:
理解了上述代码,则其它比特位操作API就容易懂了。
__clear_bit: 将addr所指的地址处的值第nr位清0,方法一般 addr[nr/32] & 11111011111
__change_bit: 将addr所指的地址处的值第nr位取反,方法一般 addr[nr/32]  ^ 00000100000
__test_and_set_bit:将addr所指的地址处的值第nr位置1,返回该bit原始值(0或1);
__test_and_clear_bit:将addr所指的地址处的值第nr位清0,返回该bit原始值(0或1);
__test_and_change_bit:将addr所指的地址处的值第nr位取反,返回该bit原始值(0或1);
test_bit:即测试nr位是否被置位,置位返回1;

3,还是在Input系统中,关于bitmap的操作。
@input.c input_match_device()函数
if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX))
bitmap_subset src1的nbits指定位数中设置1的比特位是src2中nbits指定位数中设置1的比特的子集,则返回1,否则返回0。精确到位。
关于bitmap,参考
http://blog.chinaunix.net/uid-20608849-id-3027971.html
  • bitmap_zero 清所有比特位为0,被用来初始化位图。圆整到unsigned long。
  • bitmap_fill 置nbits指定个数的比特位为1。精确到位。
  • bitmap_copy 从src复制所有比特位到dst。圆整到unsigned long。
  • bitmap_and 将nbits指定的位数按位与结果存放到dst。圆整到unsigned long。
  • bitmap_or 将nbits指定的位数按位或结果存放到dst。圆整到unsigned long。
  • bitmap_xor 将nbits指定的位数按位异或结果存放到dst。圆整到unsigned long。
  • bitmap_andnot 根据nbits指定的位数, 将src1按位与上src2的按位非,结果存放到dst。圆整到unsigned long。
  • bitmap_complement 根据nbits指定的位数, 按位取反后存放到dst。精确到位。
  • bitmap_equal 比较nbits指定的位数,如果这些为全部相同返回1,否则返回0。精确到位。
  • bitmap_intersects 比较nbits指定的位数中是否有重合(相交Overlap)的1比特位,也即src1和src2中有共同设置为1的标志位。有则返回1,否则返回0。精确到位。
  • bitmap_subset src1的nbits指定位数中设置1的比特位是src2中nbits指定位数中设置1的比特的子集,则返回1,否则返回0。精确到位。
  • bitmap_empty 测试src中的低nbits位是否全为0,是则返回1,否则返回0。精确到位。
  • bitmap_full 测试src中的低nbits位是否全为1,是则返回1,否则返回0。精确到位。
  • bitmap_weight 返回低nbits位的汉明重量(Hamming Weight)。
  • bitmap_shift_right 逻辑右移n位,左边补0。n可以大于nbits数。圆整到unsigned long。
  • bitmap_shift_left 逻辑左移n位,右边补0。n可以大于nbits数。圆整到unsigned long。

Kernel散记——常见宏