本文主要介绍vec函数的基本使用及内存分布情况,以及在工作中遇到的一些坑来分享一下。
Vectoe支持动态调整数组的大小和支持用户自定义头部,在vppinfra 其他数据结构中(pool heap hash)都能看到有使用,是最基本的数据结构。vectorj基础函数库也一直在更新变化,目前已经支持多numa。有些api接口也进行一些性能上优化,加入SIMA技术。 Vec基本的内存分布
User header (optional, uword aligned)
Alignment padding (if needed)
Vector length in elements
User's pointer -> Vector element 0
Vector element 1
...
Vector element N-1
#define vec_add2(V,P,N) vec_add2_ha(V,P,N,0,0)
vec的相关api返回的V指向向量第0个元素的指针。 为了避免内存分配器的抖动,通常不会直接把内存释放掉,而是将V的长度置位0,但是保留其已分配的内存,以供下次使用。下面介绍典型使用二种对齐模式:
这种模式在vpp源码中使用比较多的。大致结构如下:
#define vec_add2_ha(V,P,N,H,A) \
do { \
word _v(n) = (N); \
word _v(l) = vec_len (V); \
V = _vec_resize ((V), _v(n), (_v(l) + _v(n)) * sizeof ((V)[0]), (H), (A)); \
P = (V) + _v(l); \
} while (0)
/** \brief Add N elements to end of vector V,
return pointer to new elements in P. (no header, unspecified alignment)
@param V pointer to a vector
@param P pointer to new vector element(s)
@param N number of elements to add
@return V and P (value-result macro parameters)
*/
#define vec_add2(V,P,N) vec_add2_ha(V,P,N,0,0)
目前在最新的vpp代码中未找到使用的地方,在老版本16.9中临时存储发包的mbuf指针有使用,tx_vectors当成一个环形队列来使用,tx_ring_hdr_t存储环形队列使用情况;具体代码如下:
vec结构是最基础的类型,也是初学者很容易犯的错误:
Allocation only increases,Vector origin pointer may changer,store indexes(not pointers)!
1、vector 原始指针可能会改变,存储索引而不是指针。 主要时因为vec_add函数底层支持动态扩容(内存不足时,会进行3/2倍的扩容),扩容会改变原始v指针的指向,这点在使用中必须注意。 2、第一种说法也不是完全成立的。这种说法成立的前提是vector操作中不调用vec_del函数,否则也不能存储索引。 通过下面vec_del1的实现就可以确认;当del 索引不是最后一个时,处理逻辑是将最后一个赋值到当前需要删除的下标,并更新vec长度。由此可见存储索引也不是绝对安全的。
/** \brief Delete the element at index I
@param V pointer to a vector
@param I index to delete
*/
#define vec_del1(v,i) \
do { \
uword _vec_del_l = _vec_len (v) - 1; \
uword _vec_del_i = (i); \
if (_vec_del_i < _vec_del_l) \
(v)[_vec_del_i] = (v)[_vec_del_l]; \
_vec_len (v) = _vec_del_l; \
CLIB_MEM_POISON(vec_end(v), sizeof ((v)[0])); \
} while (0)
3、由于vec_del1删除处理逻辑,在遍历删除多个数据时,需要使用vec_foreach_backwards的方式来删除。避免出现漏删除的情况。
/** \brief Vector iterator */
#define vec_foreach(var,vec) for (var = (vec); var < vec_end (vec); var++)
/** \brief Vector iterator (reverse) */
#define vec_foreach_backwards(var,vec) \
for (var = vec_end (vec) - 1; var >= (vec); var--)
在vec_del1函数最后有个调用宏定义CLIB_MEM_POISON,特意跟踪了一下,最终调用的__asan_poison_memory_region,应该是一个内存越界的检测工具。
CLIB_MEM_POISON(vec_end(v), sizeof ((v)[0]))
#define ASAN_POISON_MEMORY_REGION(addr, size) \
__asan_poison_memory_region((addr), (size)
#define CLIB_MEM_POISON(a, s) ASAN_POISON_MEMORY_REGION((a), (s))
本文分享自 DPDK VPP源码分析 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!