bind 定時器

概述

BIND中有一些操作是定時任務,server.c的run_server函數中創建了三個定時任務,分別執行interface_timer_tick、heartbeat_timer_tickpps_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個定時任務。
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;將它加入到下一次調度中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章