libubox定时器工具
Libubox 是 OpenWrt 的一个必备的基础库,包含大小端转换、链表、MD5 、定时器等实用工具基础库。
Libubox提供的定时器工具用来实现一些简单的定时任务十分方便。
下面介绍其定时器工具的使用方法:
定时器工具主要数据结构和函数
struct uloop_timeout
{
struct list_head list;
bool pending;
uloop_timeout_handler cb;
struct timeval time;
};
struct uloop_timeout
用来描述一个定时器,cb
是该定时器的超时回调函数,time
是定时器超时时间,它是一个未来的时间。
//添加一个定时器,定时时间需要我们自己预先设定
int uloop_timeout_add(struct uloop_timeout *timeout);
//添加一个定时器,并设置超时时间,单位ms
int uloop_timeout_set(struct uloop_timeout *timeout, int msecs);
//删除一个定时器
int uloop_timeout_cancel(struct uloop_timeout *timeout);
//计算一个定时器还有多久超时,单位ms
int uloop_timeout_remaining(struct uloop_timeout *timeout);
Libubox 提供的定时器都是一次性定时器,如果需要循环触发,需要重复设置定时器。
定时器工具原理
uloop.c
中有维护一条定时器链表timeouts
static struct list_head timeouts = LIST_HEAD_INIT(timeouts);
uloop_timeout_set
和uloop_timeout_add
会将新的定时器添加到链表,并且新的定时器按照超时时间由近到远的顺序被添加进链表
- timer1 3s后超时
- timer2 6s后超时
- timer3 10后超时
它的实现方式如下:
int uloop_timeout_add(struct uloop_timeout *timeout)
{
struct uloop_timeout *tmp;
struct list_head *h = &timeouts;
if (timeout->pending)
return -1;
//遍历timeouts链表,找到超时时间比新增的定时器长的成员
list_for_each_entry(tmp, &timeouts, list) {
if (tv_diff(&tmp->time, &timeout->time) > 0) {
h = &tmp->list;
break;
}
}
//将新增定时器插入到该成员的前面
list_add_tail(&timeout->list, h);
timeout->pending = true;
return 0;
}
uloop_run
中会遍历定时器链表,对于已经超时的定时器会执行其回调函数。
static void uloop_process_timeouts(struct timeval *tv)
{
struct uloop_timeout *t;
while (!list_empty(&timeouts)) {
//取出定时器链表中第一个成员进行判断,因为这些成员是按照超时时间由近到远的顺序,所以第一个成员肯定是最先超时
t = list_first_entry(&timeouts, struct uloop_timeout, list);
if (tv_diff(&t->time, tv) > 0)
break;//未超时 直接退出
uloop_timeout_cancel(t);
if (t->cb)
t->cb(t);
}
}
uloop_process_timeouts()
每次只处理timeouts
中第一个成员(不是一次性处理完链表中所有的定时器),但是这个函数本身会被循环调用。
- uloop_timeout_cancel
int uloop_timeout_cancel(struct uloop_timeout *timeout)
{
if (!timeout->pending)
return -1;
list_del(&timeout->list);
timeout->pending = false;
return 0;
}
uloop_timeout_cancel
会将定时器从全局链表中删除,但是不会释放定时器本身的资源,如果是动态申请的内存,需要手动释放。
uloop_timeout定时器的缺陷
uloop_timeout定时器的缺陷在于struct uloop_timeout
结构体中没有一个存放私有数据的指针。
因此这个定时器只能实现一些简单的定时任务。
定时器工具实例
如下实例定义一个超时时间为2s的定时器u_tim
,并在回调函数中重新设置超时时间,使其循环触发。
循环3次后,取消这个定时器。
#include "stdio.h"
#include "uloop.h" //uloop_timeout_xxx
#include <sys/time.h> // struct timeval
static void u_tim_cb(struct uloop_timeout *timeout)
{
static int cnt = 0;
printf("Enter timer cb! second %ld cnt=%d\n",timeout->time.tv_sec,cnt);
uloop_timeout_set(timeout, 2000);
cnt++;
if( cnt == 3 )
{
uloop_timeout_cancel(timeout);
printf("timer cancel!\n");
}
}
static struct uloop_timeout u_tim = {
.cb = u_tim_cb,
};
int main()
{
uloop_init();
uloop_timeout_set(&u_tim, 2000);
printf("Enter uloop run!\n");
uloop_run();
return 0;
}