VPP程式碼閱讀中文註解--pool.h
/** @file * @brief Fixed length block allocator. Pools are built from clib vectors and bitmaps. Use pools when repeatedly allocating and freeing fixed-size data. Pools are fast, and avoid memory fragmentation. */ #ifndef included_pool_h #define included_pool_h #include <vppinfra/bitmap.h> #include <vppinfra/error.h> #include <vppinfra/mheap.h>
池基於動態陣列和點陣圖構建。主要用於需要頻繁申請和釋放固定大小記憶體的場合。先釋放到池裡面,再次申請的時候,從池裡面獲取,提升效率。這樣做,也能有效的避免記憶體碎片的產生。
typedef struct { /** Bitmap of indices of free objects. */ uword *free_bitmap; /** Vector of free indices. One element for each set bit in bitmap. */ u32 *free_indices; /* The following fields are set for fixed-size, preallocated pools */ /** Maximum size of the pool, in elements */ u32 max_elts; /** mmap segment info: base + length */ u8 *mmap_base; u64 mmap_size; } pool_header_t;
池的頭部結構。
free_bitmap 當前池裡面,哪些記憶體塊是空閒的(未被使用的,置1),哪些是正在被使用的(置0)。
free_indices 一個動態陣列,每一個元素是一個空閒塊的編號,陣列長度與上述點陣圖中置1的bit數目相同。
池可以使用預分配的記憶體構建,也可以使用動態陣列構建。下面3個元素僅用於預分配記憶體構建的池。
max_elts 當前池最多支援的元素的個數
mmap_base 當前池的記憶體首地址
mmap_size 當前池的記憶體大小,位元組數目。
/** Align pool header so that pointers are naturally aligned. */ #define pool_aligned_header_bytes \ vec_aligned_header_bytes (sizeof (pool_header_t), sizeof (void *)) /** Get pool header from user pool pointer */ always_inline pool_header_t * pool_header (void *v) { return vec_aligned_header (v, sizeof (pool_header_t), sizeof (void *)); }
記憶體池頭部大小(含動態陣列頭部)
以及根據0號記憶體塊地址算出的記憶體池頭部首地址
extern void _pool_init_fixed (void **, u32, u32);
extern void fpool_free (void *);
/** initialize a fixed-size, preallocated pool */
#define pool_init_fixed(pool,max_elts) \
{ \
_pool_init_fixed((void **)&(pool),sizeof(pool[0]),max_elts); \
}
pool.c中說明
/** Validate a pool */
always_inline void
pool_validate (void *v)
{
pool_header_t *p = pool_header (v);
uword i, n_free_bitmap;
if (!v)
return;
n_free_bitmap = clib_bitmap_count_set_bits (p->free_bitmap);
ASSERT (n_free_bitmap == vec_len (p->free_indices));
for (i = 0; i < vec_len (p->free_indices); i++)
ASSERT (clib_bitmap_get (p->free_bitmap, p->free_indices[i]) == 1);
}
對記憶體池的資料結構進行校驗,主要判斷是否遭到了破壞。在開發和執行階段能更快定位問題。
1. 點陣圖中置1的數目與空閒記憶體塊向量長度必須相等。
2. 點陣圖中置1的bit的編號與空閒記憶體塊中的編號必須一 一對應。
always_inline void
pool_header_validate_index (void *v, uword index)
{
pool_header_t *p = pool_header (v);
if (v)
vec_validate (p->free_bitmap, index / BITS (uword));
}
#define pool_validate_index(v,i) \
do { \
uword __pool_validate_index = (i); \
vec_validate_ha ((v), __pool_validate_index, \
pool_aligned_header_bytes, /* align */ 0); \
pool_header_validate_index ((v), __pool_validate_index); \
} while (0)
給出需要使用的記憶體塊編號,如果此記憶體塊不存在,則需要動態擴充套件記憶體塊向量,以及點陣圖向量
/** Number of active elements in a pool.
* @return Number of active elements in a pool
*/
always_inline uword
pool_elts (void *v)
{
uword ret = vec_len (v);
if (v)
ret -= vec_len (pool_header (v)->free_indices);
return ret;
}
當前正在使用的記憶體塊數量等於池中總的記憶體塊數量減去空閒狀態的記憶體塊數量
/** Number of elements in pool vector.
@note You probably want to call pool_elts() instead.
*/
#define pool_len(p) vec_len(p)
/** Number of elements in pool vector (usable as an lvalue)
@note You probably don't want to use this macro.
*/
#define _pool_len(p) _vec_len(p)
當前記憶體池中總的記憶體塊數量
/** Memory usage of pool header. */
always_inline uword
pool_header_bytes (void *v)
{
pool_header_t *p = pool_header (v);
if (!v)
return 0;
return vec_bytes (p->free_bitmap) + vec_bytes (p->free_indices);
}
/** Memory usage of pool. */
#define pool_bytes(P) (vec_bytes (P) + pool_header_bytes (P))
計算記憶體池的記憶體佔用。實際上有3大塊。
記憶體池所有記憶體塊的佔用
空閒記憶體塊編號向量的佔用
空閒記憶體塊點陣圖向量的佔用
/** Local variable naming macro. */
#define _pool_var(v) _pool_##v
用於函式巨集中的區域性變數的命名,防止多個巨集同時使用時,命名衝突。
/** Queries whether pool has at least N_FREE free elements. */
always_inline uword
pool_free_elts (void *v)
{
pool_header_t *p = pool_header (v);
uword n_free = 0;
if (v)
{
n_free += vec_len (p->free_indices);
/* Space left at end of vector? */
n_free += vec_capacity (v, sizeof (p[0])) - vec_len (v);
}
return n_free;
}
計算當前記憶體池空間中,除了已經被使用的記憶體塊,還可以分配的記憶體塊數目。這裡分為了2部分。一部分是當前處於空閒狀態的記憶體塊;另外一部分是在當前總記憶體空間中(之前已經向系統申請),還沒有被納入記憶體池中的記憶體空間。
/** Allocate an object E from a pool P (general version).
First search free list. If nothing is free extend vector of objects.
*/
#define pool_get_aligned(P,E,A) \
do { \
pool_header_t * _pool_var (p) = pool_header (P); \
uword _pool_var (l); \
\
STATIC_ASSERT(A==0 || ((A % sizeof(P[0]))==0) || ((sizeof(P[0]) % A) == 0), \
"Pool aligned alloc of incorrectly sized object"); \
_pool_var (l) = 0; \
if (P) \
_pool_var (l) = vec_len (_pool_var (p)->free_indices); \
\
if (_pool_var (l) > 0) \
{ \
/* Return free element from free list. */ \
uword _pool_var (i) = _pool_var (p)->free_indices[_pool_var (l) - 1]; \
(E) = (P) + _pool_var (i); \
_pool_var (p)->free_bitmap = \
clib_bitmap_andnoti_notrim (_pool_var (p)->free_bitmap, \
_pool_var (i)); \
_vec_len (_pool_var (p)->free_indices) = _pool_var (l) - 1; \
} \
else \
{ \
/* fixed-size, preallocated pools cannot expand */ \
if ((P) && _pool_var(p)->max_elts) \
{ \
clib_warning ("can't expand fixed-size pool"); \
os_out_of_memory(); \
} \
/* Nothing on free list, make a new element and return it. */ \
P = _vec_resize (P, \
/* length_increment */ 1, \
/* new size */ (vec_len (P) + 1) * sizeof (P[0]), \
pool_aligned_header_bytes, \
/* align */ (A)); \
E = vec_end (P) - 1; \
} \
} while (0)
/** Allocate an object E from a pool P (unspecified alignment). */
#define pool_get(P,E) pool_get_aligned(P,E,0)
從記憶體池中獲取記憶體塊。如果有空閒記憶體塊,則先從空閒記憶體塊獲取,否則先擴充套件記憶體池,再獲取記憶體塊。
/** See if pool_get will expand the pool or not */
#define pool_get_aligned_will_expand(P,YESNO,A) \
do { \
pool_header_t * _pool_var (p) = pool_header (P); \
uword _pool_var (l); \
\
_pool_var (l) = 0; \
if (P) \
{ \
if (_pool_var (p)->max_elts) \
_pool_var (l) = _pool_var (p)->max_elts; \
else \
_pool_var (l) = vec_len (_pool_var (p)->free_indices); \
} \
\
/* Free elements, certainly won't expand */ \
if (_pool_var (l) > 0) \
YESNO=0; \
else \
{ \
/* Nothing on free list, make a new element and return it. */ \
YESNO = _vec_resize_will_expand \
(P, \
/* length_increment */ 1, \
/* new size */ (vec_len (P) + 1) * sizeof (P[0]), \
pool_aligned_header_bytes, \
/* align */ (A)); \
} \
} while (0)
判斷如果從記憶體池中獲取一個記憶體塊,是否需要進行記憶體擴充套件。這裡的擴充套件的意思是:是否需要申請新的記憶體。與上一個場景中擴充套件的概念不同,上一個指的是陣列大小是否變大。
#define pool_get_will_expand(P,YESNO) pool_get_aligned_will_expand(P,YESNO,0)
簡單封裝
/** Use free bitmap to query whether given element is free. */
#define pool_is_free(P,E) \
({ \
pool_header_t * _pool_var (p) = pool_header (P); \
uword _pool_var (i) = (E) - (P); \
(_pool_var (i) < vec_len (P)) ? clib_bitmap_get (_pool_var (p)->free_bitmap, _pool_i) : 1; \
})
根據點陣圖來判斷,某記憶體塊是否空閒的
/** Use free bitmap to query whether given index is free */
#define pool_is_free_index(P,I) pool_is_free((P),(P)+(I))
簡單封裝,I 為記憶體塊的編號
/** Free an object E in pool P. */
#define pool_put(P,E) \
do { \
pool_header_t * _pool_var (p) = pool_header (P); \
uword _pool_var (l) = (E) - (P); \
ASSERT (vec_is_member (P, E)); \
ASSERT (! pool_is_free (P, E)); \
\
/* Add element to free bitmap and to free list. */ \
_pool_var (p)->free_bitmap = \
clib_bitmap_ori_notrim (_pool_var (p)->free_bitmap, \
_pool_var (l)); \
\
/* Preallocated pool? */ \
if (_pool_var (p)->max_elts) \
{ \
ASSERT(_pool_var(l) < _pool_var (p)->max_elts); \
_pool_var(p)->free_indices[_vec_len(_pool_var(p)->free_indices)] = \
_pool_var(l); \
_vec_len(_pool_var(p)->free_indices) += 1; \
} \
else \
vec_add1 (_pool_var (p)->free_indices, _pool_var (l)); \
} while (0)
/** Free pool element with given index. */
#define pool_put_index(p,i) \
do { \
typeof (p) _e = (p) + (i); \
pool_put (p, _e); \
} while (0)
釋放記憶體塊到記憶體池中,其實就是幹2件事情。1,修改空閒點陣圖;2,向空閒塊向量增加元素。需要注意的是,如果是預分配的記憶體池,則不能再重新動態分配記憶體,所以向空閒塊增加元素時,有一個上限(不能超過max_elts)。
/** Allocate N more free elements to pool (general version). */
#define pool_alloc_aligned(P,N,A) \
do { \
pool_header_t * _p; \
\
if ((P)) \
{ \
_p = pool_header (P); \
if (_p->max_elts) \
{ \
clib_warning ("Can't expand fixed-size pool"); \
os_out_of_memory(); \
} \
} \
\
(P) = _vec_resize ((P), 0, (vec_len (P) + (N)) * sizeof (P[0]), \
pool_aligned_header_bytes, \
(A)); \
_p = pool_header (P); \
vec_resize (_p->free_indices, (N)); \
_vec_len (_p->free_indices) -= (N); \
} while (0)
/** Allocate N more free elements to pool (unspecified alignment). */
#define pool_alloc(P,N) pool_alloc_aligned(P,N,0)
記憶體池擴容N個記憶體塊。這個時候,空閒記憶體塊向量也一併擴容,但len不變,只是把記憶體空間先申請到,免得後面申請不到空間。其實點陣圖記憶體塊也可以先把記憶體佔到,但是這裡並沒有這樣做,畢竟點陣圖佔用的記憶體還是比較少,後面使用到時再申請。
/** Low-level free pool operator (do not call directly). */
always_inline void *
_pool_free (void *v)
{
pool_header_t *p = pool_header (v);
if (!v)
return v;
clib_bitmap_free (p->free_bitmap);
if (p->max_elts)
{
int rv;
rv = munmap (p->mmap_base, p->mmap_size);
if (rv)
clib_unix_warning ("munmap");
}
else
{
vec_free (p->free_indices);
vec_free_h (v, pool_aligned_header_bytes);
}
return 0;
}
/** Free a pool. */
#define pool_free(p) (p) = _pool_free(p)
銷燬記憶體池
/** Optimized iteration through pool.
@param LO pointer to first element in chunk
@param HI pointer to last element in chunk
@param POOL pool to iterate across
@param BODY operation to perform
Optimized version which assumes that BODY is smart enough to
process multiple (LOW,HI) chunks. See also pool_foreach().
*/
#define pool_foreach_region(LO,HI,POOL,BODY) \
do { \
uword _pool_var (i), _pool_var (lo), _pool_var (hi), _pool_var (len); \
uword _pool_var (bl), * _pool_var (b); \
pool_header_t * _pool_var (p); \
\
_pool_var (p) = pool_header (POOL); \
_pool_var (b) = (POOL) ? _pool_var (p)->free_bitmap : 0; \
_pool_var (bl) = vec_len (_pool_var (b)); \
_pool_var (len) = vec_len (POOL); \
_pool_var (lo) = 0; \
\
for (_pool_var (i) = 0; \
_pool_var (i) <= _pool_var (bl); \
_pool_var (i)++) \
{ \
uword _pool_var (m), _pool_var (f); \
_pool_var (m) = (_pool_var (i) < _pool_var (bl) \
? _pool_var (b) [_pool_var (i)] \
: 1); \
while (_pool_var (m) != 0) \
{ \
_pool_var (f) = first_set (_pool_var (m)); \
_pool_var (hi) = (_pool_var (i) * BITS (_pool_var (b)[0]) \
+ min_log2 (_pool_var (f))); \
_pool_var (hi) = (_pool_var (i) < _pool_var (bl) \
? _pool_var (hi) : _pool_var (len)); \
_pool_var (m) ^= _pool_var (f); \
if (_pool_var (hi) > _pool_var (lo)) \
{ \
(LO) = _pool_var (lo); \
(HI) = _pool_var (hi); \
do { BODY; } while (0); \
} \
_pool_var (lo) = _pool_var (hi) + 1; \
} \
} \
} while (0)
/** Iterate through pool.
@param VAR A variable of same type as pool vector to be used as an
iterator.
@param POOL The pool to iterate across.
@param BODY The operation to perform, typically a code block. See
the example below.
This macro will call @c BODY with each active pool element.
It is a bad idea to allocate or free pool element from within
@c pool_foreach. Build a vector of indices and dispose of them later.
Or call pool_flush.
@par Example
@code{.c}
proc_t *procs; // a pool of processes.
proc_t *proc; // pointer to one process; used as the iterator.
pool_foreach (proc, procs, ({
if (proc->state != PROC_STATE_RUNNING)
continue;
// check a running proc in some way
...
}));
@endcode
@warning Because @c pool_foreach is a macro, syntax errors can be
difficult to find inside @c BODY, let alone actual code bugs. One
can temporarily split a complex @c pool_foreach into a trivial
@c pool_foreach which builds a vector of active indices, and a
vec_foreach() (or plain for-loop) to walk the active index vector.
*/
#define pool_foreach(VAR,POOL,BODY) \
do { \
uword _pool_foreach_lo, _pool_foreach_hi; \
pool_foreach_region (_pool_foreach_lo, _pool_foreach_hi, (POOL), \
({ \
for ((VAR) = (POOL) + _pool_foreach_lo; \
(VAR) < (POOL) + _pool_foreach_hi; \
(VAR)++) \
do { BODY; } while (0); \
})); \
} while (0)
遍歷記憶體池中記憶體塊的快速版本和普通版本。執行使用者指定的操作BODY。
主要利用了點陣圖的特性,一個uword為0,則uword內所有bit都為0。
找出連續的0 bit, 分別對他們進行遍歷。
其實遍歷過程是這樣分成trunk進行遍歷的。 10000 1 1 1000 10 1 100 1 , 每一個trunk中最多隻設定了1個bit。
然後實際執行使用者的BODY函式時,只針對0bit進行處理。
/** Returns pointer to element at given index.
ASSERTs that the supplied index is valid.
Even though one can write correct code of the form
@code
p = pool_base + index;
@endcode
use of @c pool_elt_at_index is strongly suggested.
*/
#define pool_elt_at_index(p,i) \
({ \
typeof (p) _e = (p) + (i); \
ASSERT (! pool_is_free (p, _e)); \
_e; \
})
返回第 i 號記憶體塊的地址。
/** Return next occupied pool index after @c i, useful for safe iteration. */
#define pool_next_index(P,I) \
({ \
pool_header_t * _pool_var (p) = pool_header (P); \
uword _pool_var (rv) = (I) + 1; \
\
_pool_var(rv) = \
(_pool_var (rv) < vec_len (P) ? \
clib_bitmap_next_clear (_pool_var (p)->free_bitmap, _pool_var(rv)) \
: ~0); \
_pool_var(rv) = \
(_pool_var (rv) < vec_len (P) ? \
_pool_var (rv) : ~0); \
_pool_var(rv); \
})
從第i個記憶體塊開始,獲取下一個使用中的記憶體塊。注意,使用中的記憶體塊,點陣圖中的值為0
/** Iterate pool by index. */
#define pool_foreach_index(i,v,body) \
for ((i) = 0; (i) < vec_len (v); (i)++) \
{ \
if (! pool_is_free_index ((v), (i))) \
do { body; } while (0); \
}
遍歷每一個使用中的記憶體塊,執行指定的操作
/**
* @brief Remove all elements from a pool in a safe way
*
* @param VAR each element in the pool
* @param POOL The pool to flush
* @param BODY The actions to perform on each element before it is returned to
* the pool. i.e. before it is 'freed'
*/
#define pool_flush(VAR, POOL, BODY) \
{ \
uword *_pool_var(ii), *_pool_var(dv) = NULL; \
\
pool_foreach((VAR), (POOL), \
({ \
vec_add1(_pool_var(dv), (VAR) - (POOL)); \
})); \
vec_foreach(_pool_var(ii), _pool_var(dv)) \
{ \
(VAR) = pool_elt_at_index((POOL), *_pool_var(ii)); \
do { BODY; } while (0); \
pool_put((POOL), (VAR)); \
} \
vec_free(_pool_var(dv)); \
}
以一種比較安全的方式釋放所有的記憶體塊到記憶體池中。其實就是針對沒有釋放的記憶體塊編號,申請了一個新的向量空間來儲存。
最後遍歷這個新的向量空間,再處理。這個新的向量空間起一個輔助的作用。新的向量空間不需要進行任何的保護。