VPP代码阅读中文注解--clib.h

#ifndef included_clib_h
#define included_clib_h

#include <vppinfra/config.h>

#define宏定义用于在头文件多次被包含时出现问题

此#include暂时不理会它 。

/* Standalone means to not assume we are running on a Unix box. */
#if ! defined (CLIB_STANDALONE) && ! defined (CLIB_LINUX_KERNEL)
#define CLIB_UNIX
#endif

支持的运行环境大类就2个,UNIX和非UNIX(standalone) .

UNIX大类中,又将linux内核环境区分开来。

#include <vppinfra/types.h>

此#include暂时不用理会

/* Global DEBUG flag.  Setting this to 1 or 0 turns off
   ASSERT (see vppinfra/error.h) & other debugging code. */
#ifndef CLIB_DEBUG
#define CLIB_DEBUG 0
#endif

DEBUG开关,打开或者关闭代码中的某些调试功能,学习代码过程中可以打开

#ifndef NULL
#define NULL ((void *) 0)
#endif

空指针的定义,一般空指针都这样玩

#define BITS(x)		(8*sizeof(x))
#define ARRAY_LEN(x)	(sizeof (x)/sizeof (x[0]))

一个字段所占用的的bit数目即 它占用的字节数目乘以8--因为每个字节占用8bit。 而一个对象占用的字节数目使用sizeof运算符在编译阶段求出来。

数组长度等于数组空间的总长度(字节数目) 除以  数组元素0(第1个元素)的长度(字节数目)。 结果即是数组元素的个数--即本数组共有几个成员。这也是一种经典的惯用法。相比于对长度硬编码要灵活得多(由编译器帮你计算,而不是自己人工去数)。-- 备注,也可以人工去计数数组元素个数,但容易出错,且麻烦。

#define _STRUCT_FIELD(t,f) (((t *) 0)->f)
#define STRUCT_OFFSET_OF(t,f) ((uword) & _STRUCT_FIELD (t, f))
#define STRUCT_BIT_OFFSET_OF(t,f) (BITS(u8) * (uword) & _STRUCT_FIELD (t, f))
#define STRUCT_SIZE_OF(t,f)   (sizeof (_STRUCT_FIELD (t, f)))
#define STRUCT_BITS_OF(t,f)   (BITS (_STRUCT_FIELD (t, f)))
#define STRUCT_ARRAY_LEN(t,f) ARRAY_LEN (_STRUCT_FIELD (t, f))
#define STRUCT_MARK(mark)     u8 mark[0]
#define STRUCT_MARK_PTR(v, f) &(v)->f

_STRUCT_FIELD读取结构体t中的成员f

STRUCT_OFFSET_OF求结构体t中成员f的偏移量(字节单位)---编译时求出

STRUCT_BIT_OFFSET_OF 同上(位为单位)

STRUCT_SIZE_OF求成员所占空间大小(字节单位)

STRUCT_BITS_OF 同上(位为单位)

STRUCT_ARRAY_LEN成员f所含数组元素的个数--如果f不是数组,则不要使用

STRUCT_MARK一般用于结构体的末尾,在运行时申请结构体的内存,并且超过结构体本身的大小,末尾多出的部分作为数组的大小,或者也只是一个符号记录结构体的末尾,并不额外占用内存空间。

STRUCT_MARK_PTR 不细说

/* Stride in bytes between struct array elements. */
#define STRUCT_STRIDE_OF(t,f)			\
  (  ((uword) & (((t *) 0)[1].f))		\
   - ((uword) & (((t *) 0)[0].f)))

这段代码讲的这个事情。假定有一个结构体t和结构体中的成员变量f。 那么在t类型变量形成的数组中,2个相邻的数组元素的f成员变量的内存距离是多少?通过这个表达式就求出来了。

仔细一想,其实就是sizeof(t)的值。

#define STRUCT_OFFSET_OF_VAR(v,f) ((uword) (&(v)->f) - (uword) (v))

这个宏也是算成员变量相对于结构体首地址的偏移。与STRUCT_OFFSET_OF功能一致

/* Used to pack structure elements. */
#define CLIB_PACKED(x)	x __attribute__ ((packed))
#define CLIB_UNUSED(x)	x __attribute__ ((unused))

gcc编译器扩展属性packed表示结构体内部不填充pad,采用紧密模式。缺省情况下会填充一些pad字节。

unused属性表示,被修饰的变量有可能不会被使用,针对这种情况,不要编译报警。

/* Make a string from the macro's argument */
#define CLIB_STRING_MACRO(x) #x

即 printf(CLIB_STRING_MACRO(hello)) ; 会被替换成 printf("hello");

#define __clib_unused __attribute__ ((unused))
#define __clib_weak __attribute__ ((weak))
#define __clib_packed __attribute__ ((packed))
#define __clib_constructor __attribute__ ((constructor))

weak扩展属性表示弱引用。

   若两个或两个以上全局符号(函数或变量名)名字一样,而其中之一声明为weak symbol(弱符号),则这些全局符号不会引发重定义错误。链接器会忽略弱符号,去使用普通的全局符号来解析所有对这些符号的引用,但当普通的全局符号不可用时,链接器会使用弱符号。当有函数或变量名可能被用户覆盖时,该函数或变量名可以声明为一个弱符号。弱符号也称为weak alias(弱别名)

constructor扩展属性表示被修饰的代码在main函数调用前会被执行

#define never_inline __attribute__ ((__noinline__))

被修饰的函数,请求编译器不要内联展开。

#if CLIB_DEBUG > 0
#define always_inline static inline
#define static_always_inline static inline
#else
#define always_inline static inline __attribute__ ((__always_inline__))
#define static_always_inline static inline __attribute__ ((__always_inline__))
#endif

调试开关打开的情况下,不强制内联。因为有些bug是内联导致,不方便问题定位。

普通的inline只是建议编译器进行内联。而__always_inline__则强制要求编译器进行内联展开。

/* Reserved (unused) structure element with address offset between
   from and to. */
#define CLIB_PAD_FROM_TO(from,to) u8 pad_##from[(to) - (from)]

从某个字节开始连续填充若干字节。如从第13字节填充到第15字节。

CLIB_PAD_FROM_TO(13,15)

将被替换成u8 pad_13[2]

/* Hints to compiler about hot/cold code. */
#define PREDICT_FALSE(x) __builtin_expect((x),0)
#define PREDICT_TRUE(x) __builtin_expect((x),1)

告诉编译器表达式x的值大概率是0,或者是1。主要用在if, while条件判断中,编译器根据这个提示对代码流程优化。如将大概率是1的代码块放在附近,其它代码放较远的地方。---因为指令跳转会使指令缓存中已存在的指令全部无效,重新加载指令降低效率。

/* Full memory barrier (read and write). */
#define CLIB_MEMORY_BARRIER() __sync_synchronize ()

主要告诉编译器,不要把这个标记前后的代码进行对调,在执行时,也不允许CPU将前后的指令对调后执行。如果没有这样的机制,编译器和CPU可以根据当前的实际情况,按自己的判断,选择一种它认为合适的方式处理。

#if __x86_64__
#define CLIB_MEMORY_STORE_BARRIER() __builtin_ia32_sfence ()
#else
#define CLIB_MEMORY_STORE_BARRIER() __sync_synchronize ()
#endif

同上

/* Arranges for function to be called before main. */
#define INIT_FUNCTION(decl)			\
  decl __attribute ((constructor));		\
  decl

/* Arranges for function to be called before exit. */
#define EXIT_FUNCTION(decl)			\
  decl __attribute ((destructor));		\
  decl

主要用于main函数执行前执行的代码,以及main函数返回后执行的代码的描述。但没有搜到这2个宏使用的地方。

/* Use __builtin_clz if available. */
#if uword_bits == 64
#define count_leading_zeros(x) __builtin_clzll (x)
#define count_trailing_zeros(x) __builtin_ctzll (x)
#else
#define count_leading_zeros(x) __builtin_clzl (x)
#define count_trailing_zeros(x) __builtin_ctzl (x)
#endif

64位或者32位 CPU架构下。

编译器内置的计算高位连续0bit的个数,或者低位连续0bit的个数的方法

比如64这个值的二进制是 0000...00001000000  低位连续6个0。高位连续25个0(32位CPU)或者57个0(64位CPU)

#if defined (count_leading_zeros)
always_inline uword
min_log2 (uword x)
{
  uword n;
  n = count_leading_zeros (x);
  return BITS (uword) - n - 1;
}
#else

基于编译器内置的计算连续0bit位数的结果。求x以2为底的对数的整数部分。

比如min_log2(15) 的 值 为  3。即2的3次幂为8. 2的4次幂为16。

8~15之间的所有数,它们的min_log2的值都为3。

else部分不会允许到,所以就不解释了。

always_inline uword
max_log2 (uword x)
{
  uword l = min_log2 (x);
  if (x > ((uword) 1 << l))
    l++;
  return l;
}

基于min_log2的结果。求x以2为底的对数的整数部分--向上取整。

比如max_log2(15) 的 值 为  4。即2的4次幂为16。

9~16之间的所有数,它们的max_log2的值都为4。

always_inline u64
min_log2_u64 (u64 x)
{
  if (BITS (uword) == 64)
    return min_log2 (x);
  else
    {
      uword l, y;
      y = x;
      l = 0;
      if (y == 0)
	{
	  l += 32;
	  x >>= 32;
	}
      l += min_log2 (x);
      return l;
    }
}

这里描述了如果求64位数的以2位底的对数的整数部分。

特别是else分支表示,在32位CPU上如何计算64位数的对数。

如果最低32位为0,则取高32位继续计算,结果加上32。

如果最低32位不为0,则只需要计算最低32位即可。

always_inline uword
pow2_mask (uword x)
{
  return ((uword) 1 << x) - (uword) 1;
}

最低x位连续为1,其它位为0。主要用于&位运算。具有掩码语义,名副其实。

如pow2_mask(3) == 000111   二进制表示

    pow2_mask(4) == 001111  二进制表示

always_inline uword
max_pow2 (uword x)
{
  word y = (word) 1 << min_log2 (x);
  if (x > y)
    y *= 2;
  return y;
}

向上取一个整数,这个整数必须是2的幂次方

如9~16向上取整为16 (2的4次幂), 17~32向上取整为32(2的5次幂)

always_inline uword
is_pow2 (uword x)
{
  return 0 == (x & (x - 1));
}

判断1个数是否是2的幂次方。

如0,1,2,4,8,16,32,...就是。其它数不是

always_inline uword
round_pow2 (uword x, uword pow2)
{
  return (x + pow2 - 1) & ~(pow2 - 1);
}

这里注意,pow2是一个2的幂次方数,如2,4,8,16等。x是另外一个独立的数目。

结果是将x增加一小部分,使得x的二进制表示的尾部有连续的几个0。一般用于分配内存计算字节数时多申请点内存,并且满足内存对齐约束时。调用此函数。

always_inline u64
round_pow2_u64 (u64 x, u64 pow2)
{
  return (x + pow2 - 1) & ~(pow2 - 1);
}

同上,64位版本

always_inline uword
first_set (uword x)
{
  return x & -x;
}

从x最低位算起,遇到的第一个值为1的bit保留,其它位全部清0。将结果返回。

always_inline uword
log2_first_set (uword x)
{
  uword result;
#ifdef count_trailing_zeros
  result = count_trailing_zeros (x);
#else
  result = min_log2 (first_set (x));
#endif
  return result;
}

计算x最低位连续的0bit的个数

always_inline f64
flt_round_down (f64 x)
{
  return (int) x;
}

对小数向下取整。

always_inline word
flt_round_nearest (f64 x)
{
  return (word) (x + .5);
}

对小数四舍五入取整

always_inline f64
flt_round_to_multiple (f64 x, f64 f)
{
  return f * flt_round_nearest (x / f);
}

将小数四舍五入到指定的精度

#define clib_max(x,y)				\
({						\
  __typeof__ (x) _x = (x);			\
  __typeof__ (y) _y = (y);			\
  _x > _y ? _x : _y;				\
})

#define clib_min(x,y)				\
({						\
  __typeof__ (x) _x = (x);			\
  __typeof__ (y) _y = (y);			\
  _x < _y ? _x : _y;				\
})

#define clib_abs(x)				\
({						\
  __typeof__ (x) _x = (x);			\
  _x < 0 ? -_x : _x;				\
})

求最大值,最小值,绝对值的函数宏

__typeof__(x) 表示 x变量的当前类型

__typeof__(x)  _x = (x) 表示用x的当前类型再定义一个临时变量_x,其值初始化为x现在的值。

猜你喜欢

转载自blog.csdn.net/weixin_40870382/article/details/83089159