概述
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;將它加入到下一次調度中。