DPDK自带的定时器采用跳表实现,时间复杂度是O(logn),当有大量事件要定时触发时,比如会话session老化,效率并不高。因此DPVS采用了O(1)复杂度的时间轮。

a. 添加定时器事件的核心是
static int __dpvs_timer_sched(struct timer_scheduler *sched,struct dpvs_timer *timer, struct timeval *delay,dpvs_timer_cb_t handler, void *arg, bool period)
b. rte_timer_tick_cb()中判断超时事件,调用timer_expire()执行事件注册的回调函数;
c. timer_expire()会判断事件是否循环执行,需要循环执行的,调用__dpvs_timer_sched()重新放回时间轮;
d. 一个事件的超时时间大于第一层的时间(DPVS_TIMER_HZ = 1000时,为524s),才会放到第二层,所以一般只用到第一层;
采用死循环,调用查询函数,判断是否达到时间轮的最小刻度,伪代码如下:
struct timer_scheduler *sched;
uint64_t next = 0;
uint64_t rounds = rte_get_timer_hz() / DPVS_TIMER_HZ; // 每秒的cpu圈数 / 每秒的次数 (DPVS_TIMER_HZ = 1000,rounds表示每毫秒的cpu圈数,定时器为毫秒级定时器)
for(;;) {uint64_t now;struct rte_timer *tim;now = rte_get_timer_cycles();if (now >= next) {rte_timer_tick_cb(tim, sched);next = now + rounds}
}
int dpvs_timer_sched_nolock(struct dpvs_timer *timer, struct timeval *delay,dpvs_timer_cb_t handler, void *arg, bool global)
{struct timer_scheduler *sched = this_lcore_sched(global);int err;if (!sched || !timer || !delay || !handler|| delay->tv_sec >= TIMER_MAX_SECS)return EDPVS_INVAL;err = __dpvs_timer_sched(sched, timer, delay, handler, arg, false);return err;
}
int dpvs_timer_update_nolock(struct dpvs_timer *timer, struct timeval *delay, bool global)
{struct timer_scheduler *sched = this_lcore_sched(global);int err;if (!sched || !timer || !delay)return EDPVS_INVAL;if (timer_pending(timer))list_del(&timer->list);err = __dpvs_timer_sched(sched, timer, delay,timer->handler, timer->priv, timer->is_period);return err;
}
int dpvs_timer_cancel_nolock(struct dpvs_timer *timer, bool global)
{struct timer_scheduler *sched = this_lcore_sched(global);if (!sched || !timer)return EDPVS_INVAL;if (timer_pending(timer))list_del(&timer->list);return EDPVS_OK;
}
原文链接:https://zhuanlan.zhihu.com/p/366751595
(免费订阅,永久学习)学习地址: Dpdk/网络协议栈/vpp/OvS/DDos/NFV/虚拟化/高性能专家-学习视频教程-腾讯课堂
更多DPDK相关学习资料有需要的可以自行报名学习,免费订阅,永久学习,或点击这里加qun免费
领取,关注我持续更新哦! !