定时器的启动流程
- 调用
rte_timer_subsystem_init
初始化定时器子系统 - 调用
rte_timer_init
初始化定时器结构体 - 调用
rte_timer_reset
设定定时器的类型、周期、回调函数等 - 调用
rte_timer_manage
检查已经超时的定时器,并触发相应的回调函数 - 调用
rte_timer_stop
定时定时器
rte_timer
rte_timer是dpdk中定时器结构体,用于描述一个定时器。
struct rte_timer
{
uint64_t expire; /**定时器到期时间,以HZ来描述,到期后执行call back函数*/
struct rte_timer *sl_next[MAX_SKIPLIST_DEPTH];
volatile union rte_timer_status status; /**定时器状态*/
uint64_t period; /**定时器触发周期,0表示仅触发一次*/
rte_timer_cb_t f; /**call back函数*/
void *arg; /**call back函数参数*/
};
rte_timer_subsystem_init
定时器子系统初始化。
void rte_timer_subsystem_init(void)
rte_timer_init
初始化一个定时器。
void rte_timer_init(struct rte_timer *tim)
rte_timer_reset
重置并启动定时器,通过rte_timer_reset
给rte_timer
设置参数。
int rte_timer_reset(struct rte_timer *tim, uint64_t ticks,
enum rte_timer_type type, unsigned tim_lcore,
rte_timer_cb_t fct, void *arg)
- tim : 定时器指针
- ticks : 定时器执行周期,以CPU HZ描述
- type : 定时器执行类型
- tim_lcore : 指定定时器的call back函数在哪个lcores
- rte_timer_cb_t : call back函数指针
- arg : call back函数参数
enum rte_timer_type
定时器触发方式
- SINGLE : 仅触发一次,对应将rte_timer->period置0,定时器在触发后状态被置为
RTE\_TIMER\_STOP
,可通过调用rte\_timer\_reset
再次启动。 - PERIODICAL : 周期性触发,定时器在触发后状态被置为
RTE\_TIMER\_PENDING
,下一个周期会再次触发。
enum rte_timer_type {
SINGLE,
PERIODICAL
};
rte_timer_manage
rte_timer_manager用于查询所有到期的定时器,并执行call back函数。rte_timer_manager需要被周期性调用,调用的周期决定了定时器的精度。函数原型如下:
void rte_timer_manager (void)
rte_timer_stop
停止一个定时器。
int rte_timer_stop(struct rte_timer *tim)
定时器简单实例
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2010-2014 Intel Corporation
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <sys/queue.h>
#include <rte_common.h>
#include <rte_memory.h>
#include <rte_launch.h>
#include <rte_eal.h>
#include <rte_per_lcore.h>
#include <rte_lcore.h>
#include <rte_cycles.h>
#include <rte_timer.h>
#include <rte_debug.h>
#define TIMER_RESOLUTION_CYCLES 20000000ULL /* around 10ms at 2 Ghz */
static struct rte_timer timer0;
static struct rte_timer timer1;
/* timer0 callback */
static void
timer0_cb(__attribute__((unused)) struct rte_timer *tim,
__attribute__((unused)) void *arg)
{
static unsigned counter = 0;
unsigned lcore_id = rte_lcore_id();
printf("%s() on lcore %u\n", __func__, lcore_id);
/* this timer is automatically reloaded until we decide to
* stop it, when counter reaches 20. */
if ((counter ++) == 20)
rte_timer_stop(tim);
}
/* timer1 callback */
static void
timer1_cb(__attribute__((unused)) struct rte_timer *tim,
__attribute__((unused)) void *arg)
{
unsigned lcore_id = rte_lcore_id();
uint64_t hz;
printf("%s() on lcore %u\n", __func__, lcore_id);
/* reload it on another lcore */
hz = rte_get_timer_hz();
lcore_id = rte_get_next_lcore(lcore_id, 0, 1);
rte_timer_reset(tim, hz/3, SINGLE, lcore_id, timer1_cb, NULL);
}
static __attribute__((noreturn)) int
lcore_mainloop(__attribute__((unused)) void *arg)
{
uint64_t prev_tsc = 0, cur_tsc, diff_tsc;
unsigned lcore_id;
lcore_id = rte_lcore_id();
printf("Starting mainloop on core %u\n", lcore_id);
while (1) {
/*
* Call the timer handler on each core: as we don't
* need a very precise timer, so only call
* rte_timer_manage() every ~10ms (at 2Ghz). In a real
* application, this will enhance performances as
* reading the HPET timer is not efficient.
*/
cur_tsc = rte_rdtsc();
diff_tsc = cur_tsc - prev_tsc;
if (diff_tsc > TIMER_RESOLUTION_CYCLES) {
rte_timer_manage();
prev_tsc = cur_tsc;
}
}
}
int
main(int argc, char **argv)
{
int ret;
uint64_t hz;
unsigned lcore_id;
/* init EAL */
ret = rte_eal_init(argc, argv);
if (ret < 0)
rte_panic("Cannot init EAL\n");
/* init RTE timer library */
rte_timer_subsystem_init();
/* init timer structures */
rte_timer_init(&timer0);
rte_timer_init(&timer1);
/*
* PERIODICAL : 定时器触发完了之后自动加载
* SINGLE : 定时器仅触发一次
*/
/* load timer0, every second, on master lcore, reloaded automatically */
hz = rte_get_timer_hz();//CPU的赫兹数,即每秒跳了多少次
printf("hz : %llu\n", hz);
lcore_id = rte_lcore_id();
printf("timer0 reset in core %d\n", lcore_id);
rte_timer_reset(&timer0, hz, PERIODICAL, lcore_id, timer0_cb, NULL);
/* load timer1, every second/3, on next lcore, reloaded manually */
lcore_id = rte_get_next_lcore(lcore_id, 0, 1);
printf("timer1 reset in core %d\n", lcore_id);
rte_timer_reset(&timer1, hz/3, SINGLE, lcore_id, timer1_cb, NULL);//可以看出定时器可以在一个核加载,在另一个核执行call back函数
/* call lcore_mainloop() on every slave lcore */
//RTE_LCORE_FOREACH_SLAVE只会历遍所有的slave核,不包括master核
RTE_LCORE_FOREACH_SLAVE(lcore_id) {
rte_eal_remote_launch(lcore_mainloop, NULL, lcore_id);
}
/* call it on master lcore too */
(void) lcore_mainloop(NULL);
return 0;
}