intel dpdk api timer 模块详解和测试

来源:互联网 发布:ubuntu 杀死所有进程 编辑:程序博客网 时间:2024/06/06 03:41

声明:此文档只做学习交流使用,请勿用作其他商业用途

author:朝阳_tony
E-mail : linzhaolover@gmail.com
Create Date: 2013-7-22 17:36:51  Monday
Last Change: 2013-7-23 11:04:07 Tuesday

转载请注明出处:http://blog.csdn.net/linzhaolove


此文中源码可以去http://dpdk.org/dev 网页中下载;更多官方文档请访问http://dpdk.org


模块详细介绍

在dpdk库中提供了一个timer模块,负责异步调用一些回调函数,定时操作等等;

我以dpdk提供的实例模块dpdk/examples/timer  为例进行讲解 ;

1、timer初始化

1)、初始化hpet模块

首先涉及到的是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

2)、初始化定时器子系统

调用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;


获取hpet的时钟滴答,也就是一秒值;
hz = rte_get_hpet_hz();

2、启动定时器

1)、定时器的启动

    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是指只执行一次,如想让它再运行就需要从新初始化它;

2)、定时器的调度管理

    /* 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.000cpu MHz         : 2793.000cpu MHz         : 2793.000

3、停止定时器

1)、周期性定时器的停止

在启动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;
最后将其状态改为,停止状态;

2)、单次定时器的重新启动

单次的定时器执行完一次就停止,所以我就讲它怎样再启动;

在启动定时器时,我们传递了一个参数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秒调用一次,我要而是一下到底是否准确,目测肯定不行,只能用代码粗略测试;

1、添加测试代码

我用的是clock_gettime()函数,去获取每次调用timer0_cb是获取时间;但用clock_gettime()函数,要在编译的时候添加   -lrt  这个链接库;

添加头文件

#include <unistd.h>#include <time.h>
在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 += -O3CFLAGS += $(WERROR_FLAGS)#add linzhLDLIBS += -lrt


2、编译测试

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的值,发现基本上是一秒打印一次;
clock_time.tv_sec = 1374494900 tv_nsec =514227370timer0_cb() on lcore 0clock_time.tv_sec = 1374494901 tv_nsec =519700272timer0_cb() on lcore 0clock_time.tv_sec = 1374494902 tv_nsec =525173801timer0_cb() on lcore 0



技术水平有待提高,如果文章有错误的地方希望读者指正,相互交流,互相学习;O(∩_∩)O~