声明:此文档只做学习交流使用,请勿用作其他商业用途
author:朝阳_tony转载请注明出处:http://blog.csdn.net/linzhaolove
此文中源码可以去http://dpdk.org/dev 网页中下载;更多官方文档请访问http://dpdk.org
在dpdk库中提供了一个timer模块,负责异步调用一些回调函数,定时操作等等;
我以dpdk提供的实例模块dpdk/examples/timer 为例进行讲解 ;
首先涉及到的是hpet 模块的初始化,dpdk中的精准的定时是依靠hpet模块的,
在main()->rte_eal_init()->rte_eal_hpet_init() 调用rte_eal_hpet_init() 去初始化,关于dpdk中的hpet的详细初始化请看我之前写的文章http://blog.csdn.net/linzhaolove/article/details/9302655
调用rte_timer_subsystem_init去初始化timer模块;函数在dpdk/lib/librte_timer/rte_timer.c文件中;
/* init RTE timer library */
rte_timer_subsystem_init();
我们去看一下rte_timer_subsystem_init的源码;
for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id ++) {
LIST_INIT(&priv_timer[lcore_id].pending);
LIST_INIT(&priv_timer[lcore_id].expired);
LIST_INIT(&priv_timer[lcore_id].done);
rte_spinlock_init(&priv_timer[lcore_id].list_lock);
priv_timer[lcore_id].prev_lcore = lcore_id;
}
他在每个逻辑core上初始化了三个列表分别是pending , expired,done, 初始化了一个自旋锁 list_lock;
/* init timer structures */
rte_timer_init(&timer0);
rte_timer_init(&timer1);
初始化两个结构体分别是timer0 timer1, 我们想利用两个定时器;
在rte_timer_init函数中设置定时器的状态;定时器停止状态,没有宿主;
status.state = RTE_TIMER_STOP;
status.owner = RTE_TIMER_NO_OWNER;
tim->status.u32 = status.u32;
hz = rte_get_hpet_hz();
lcore_id = rte_lcore_id();
rte_timer_reset(&timer0, hz, PERIODICAL, lcore_id, timer0_cb, NULL);
通过rte_lcore_id()函数获取当前线程的core id,然后通过rte_timer_reset函数启动定时器;
我们看一下rte_timer_reset()函数的参数,
timer0 是我们上面初始的结构体;
hz 是给定时器定的时间;
PERIODICAL 是周期的意思,也是这个定时器只初始化一次然后,周期循环;
lcore_id core id , 是指定这个定时器在那个core上面运行;
timer0_cb 是我们设置的定时器到了后,调用的回调函数地址;
NULL 是给回调函数传递的参数,我们暂时用不到,所以传递的为NULL,
/* load timer1, every second/3, on next lcore, reloaded manually */
lcore_id = rte_get_next_lcore(lcore_id, 0, 1);
rte_timer_reset(&timer1, hz/3, SINGLE, lcore_id, timer1_cb, NULL);
接下来是初始化的另一个定时器timer1;这用hz/3 是指定时只有1秒的三分之一;SINGLE是指只执行一次,如想让它再运行就需要从新初始化它;
/* call lcore_mainloop() on every slave lcore */
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);
除了在主core调用lcore_mainloop() ,还要求在其他每个core调用;在里面去管理时钟;因为在一开始初始化时,每个core都初始化了三个 列表和一个独立的自旋锁;
我们看一lcore_mainloop()内部源码;
cur_tsc = rte_rdtsc();
diff_tsc = cur_tsc - prev_tsc;
if (diff_tsc > TIMER_RESOLUTION_CYCLES) {
rte_timer_manage();
prev_tsc = cur_tsc;
}
函数中会每10ms执行一次调度,让rte_timer_manage()管理当前core上的定时器‘;
#define TIMER_RESOLUTION_CYCLES 20000000ULL /* around 10ms at 2 Ghz */
这个时钟分辨率应该是你的cpu 赫兹;可以用去 /proc/cpuinfo去查看;记得要最后乘上10,希望10ms查询一次;例如
# cat /proc/cpuinfo | grep "cpu MHz"
cpu MHz : 2793.000
cpu MHz : 2793.000
cpu MHz : 2793.000
在启动timer0的时候,我们采用的是周期性的定时器,当前的回调函数是timer0_cb();
我看一下timer0_cb这个函数的源码;
/* this timer is automatically reloaded until we decide to
* stop it, when counter reaches 20. */
if ((counter ++) == 20)
rte_timer_stop(tim);
源码注释写的很清楚,当这个回调函数被执行20 次后,就会被停止,调用rte_timer_stop()函数;
简单介绍一下是怎样停止一个定时器的,看rte_timer_stop()源码;
ret = timer_set_config_state(tim, &prev_status);
首先将定时器置于配置状态;
timer_del(tim, prev_status.owner, 0);
其次将其从列表中删除;
status.state = RTE_TIMER_STOP;
status.owner = RTE_TIMER_NO_OWNER;
tim->status.u32 = status.u32;
最后将其状态改为,停止状态;
单次的定时器执行完一次就停止,所以我就讲它怎样再启动;
在启动定时器时,我们传递了一个参数SINGLE,就是指让这个定时器在某个core上执行一次,然后停止;
当前启动调用的回调函数是timer1_cb()
在timer1_cb函数中再次执行一次rte_timer_reset就可以启动定时器,
/* reload it on another lcore */
hz = rte_get_hpet_hz();
lcore_id = rte_get_next_lcore(lcore_id, 0, 1);
rte_timer_reset(tim, hz/3, SINGLE, lcore_id, timer1_cb, NULL);
但大家注意下,这个是传递的lcore_id,并不是自身的core_id ;而是通过rte_get_next_lcore()获取了另一个使能的core id , 从这个可以说明,dpdk的定时器,可以从一个core 上启动其他core上的定时器;
在启动定时器timer0的时候,设置的是1秒调用一次,我要而是一下到底是否准确,目测肯定不行,只能用代码粗略测试;
我用的是clock_gettime()函数,去获取每次调用timer0_cb是获取时间;但用clock_gettime()函数,要在编译的时候添加 -lrt 这个链接库;
添加头文件
#include
#include
在timer0_cb添加下面代码
struct timespec clock_time;
clock_gettime(CLOCK_REALTIME, &clock_time);
fprintf(stdout,"clock_time.tv_sec = %lu tv_nsec =%lu\n",clock_time.tv_sec,clock_time.tv_nsec);
添加编译链接
打开examples/timer/Makefile 文件,将编译链接添加在这下面," LDLIBS += -lrt " 例如:
CFLAGS += -O3
CFLAGS += $(WERROR_FLAGS)
#add linzh
LDLIBS += -lrt
make -C x86_64-default-linuxapp-gcc/
make -C examples/timer/
测试
./examples/helloworld/build/app/helloworld -c 1f -n 4 --proc-type=auto
clock_time.tv_sec = 1374494900 tv_nsec =514227370
timer0_cb() on lcore 0
clock_time.tv_sec = 1374494901 tv_nsec =519700272
timer0_cb() on lcore 0
clock_time.tv_sec = 1374494902 tv_nsec =525173801
timer0_cb() on lcore 0
技术水平有待提高,如果文章有错误的地方希望读者指正,相互交流,互相学习;O(∩_∩)O~