概述
BIND中有一些操作是定时任务,server.c的run_server函数中创建了三个定时任务,分别执行interface_timer_tick、heartbeat_timer_tick和pps_timer_tick;其他模块中还有很多时间任务。定时器的实现文件时timer.h和timer.c,位于lib/isc目录下。
数据结构
struct isc_timermgr {
/* Not locked. */
unsigned int magic;
isc_mem_t * mctx;
isc_mutex_t lock;
/* Locked by manager lock. */
isc_boolean_t done;
LIST(isc_timer_t) timers;
unsigned int nscheduled;
isc_time_t due;
#ifdef ISC_PLATFORM_USETHREADS
isc_condition_t wakeup;
isc_thread_t thread;
#else /* ISC_PLATFORM_USETHREADS */
unsigned int refs;
#endif /* ISC_PLATFORM_USETHREADS */
isc_heap_t * heap;
};
mtx是timermgr的内存分配器,它负责timermgr的内存分配和回收;
lock是timermgr的互斥锁,用来在多线程中保护done,timers,nscheduled等数据;
timers是timermgr管理timer,BIND中分三类timer,分别是定时执行的ticker、只执行一次的once、限制执行次数的limited和非活动的inactive,刚创建的timer都是inactive类型的,之后用isc_timer_reset将其变为前三种;
due是该事件管理器中所有时间的最小的到期时间(绝对时间);
wakeup是用来实现定时任务的条件变量;
thread是执行定时任务调度的线程;
refs是该timermgr的引用计数
heap是一些供调度的timer按照过期时间组成的一个小根堆。
调用关系
定时器管理器ns_g_timermgr只在server.c的create_managers中创建一次,创建时调用isc_timermgr_create函数。
定时器管理器内部调用关系
isc_timermgr_create的说明 :
初始化各变量;
启用线程thread执行函数run。
run线程是真正执行时间调度的线程,它获取当前的绝对时间now,然后调用dispatch做时间调度,之后如果没有要调度的时间了就用pthread_cond_wait等待直到有新的时间到达;如果还有要调度的时间,则调用pthread_cond_timedwait等待最近的那个timer的到期时间(保存在manager->due中)。
dispatch(manage, now)是真正做timer调度的函数,它从manager的堆heap中取出堆顶的timer,然后判断当前时间now是否大于了timer的过期时间due,如果不大于,则更新manager->due;如果now超过了timer的过期时间,则根据timer的类型做各种判断,判断是否需要重新调度,判断是否需要为此分发事件…..如果需要为此timer分发事件,则调用isc_event_allocate新建一个事件,然后调用isc_task_send将此event分发到任务系统中,此时任务系统中监听的线程就会执行为此timer绑定的函数。之后将此timer从manager->heap中取出,将它管理的timer数量减一,如果需要重新调度,则调用schedule重新调度此timer。
schedule:它的主要任务是将一个timer加入到调度堆中。中间需要做一些时间的修正,如果需要唤醒thread线程,则调用pthread_cond_signal唤醒thread线程。
deschedule,它将一个线程从manager->heap中删除,并唤醒thread线程
定时器管理器销毁
时间类
创建3个定时任务。
时间对象timer是timer manager管理的单位,BIND中分三类timer,分别是定时执行的ticker、只执行一次的once、限制执行次数的limited和非活动的inactive,刚创建的timer一般都是inactive类型的,之后用isc_timer_reset将其变为前三种中的一种。
下面看看timer的数据结构:
struct isc_timer {
/*! Not locked. */
unsigned int magic;
isc_timermgr_t * manager;
isc_mutex_t lock;
/*! Locked by timer lock. */
unsigned int references;
isc_time_t idle;
/*! Locked by manager lock. */
isc_timertype_t type;
isc_time_t expires;
isc_interval_t interval;
isc_task_t * task;
isc_taskaction_t action;
void * arg;
unsigned int index;
isc_time_t due;
LINK(isc_timer_t) link;
};
ticker类型ticker类型的timer每隔interval被分发到事件驱动中
server中有三个ticker类型的timer,他们分别是interface_timer, heartbeat_timer和pps_timer拿interface_timer做分析:
1,首先在run_server中被初始化:
在isc_timer_create中,为它初始化各成员后,只是简单地将它加入到timer manager的timers队列中。
2,之后在load_configuration中被reset为ticker类型
isc_timer_reset(server->interface_timer, isc_timertype_ticker,NULL, &interval, ISC_FALSE)在isc_timter_reset中,为它的interval成员赋值&interval
3,然后调用schedule将其加入调度队列:
schedule(timer, &now, ISC_TRUE)
在schedule中,为其计算到期时间due;如果它未被调度过,就调用isc_heap_insert(manager->heap, timer)将它插入到timer manager的调度堆heap中,将它从堆中floatup;最后根据情况唤醒timer manager中执行run的thread线程
4,在thead线程中,线程首先检查它的到期时间timer->due是否到如果到了,则为它分配新的事件,然后调用isc_task_send(timer->task,ISC_EVENT_PTR(&event))将它加入到事件驱动器中。把它从heap中取出,然后重新调用schedule;将它加入到下一次调度中。