1. 程式人生 > >VPP程式碼閱讀中文註解--clib.h

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現在的值。