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