調(diào)整系統(tǒng)時(shí)鐘導(dǎo)致ACE定時(shí)器丟失

字號(hào):

由于我們采用的服務(wù)器一般都是靠紐扣電池作為能源驅(qū)動(dòng)和記錄時(shí)鐘,一般在運(yùn)行一段時(shí)間后都會(huì)出現(xiàn)時(shí)間誤差。所以很多大規(guī)模的分布系統(tǒng)都有校時(shí)操作,特別是一些對(duì)時(shí)鐘要求精確的分布式系統(tǒng)(比如計(jì)費(fèi)等),往往都會(huì)有一個(gè)主機(jī)提供精確時(shí)鐘服務(wù)(其可能采用GPS校時(shí)),其他服務(wù)器通過這臺(tái)服務(wù)器校時(shí),校時(shí)操作一般都是直接改變系統(tǒng)時(shí)鐘。
    ACE的定時(shí)器都是采用Event_Handler進(jìn)行處理,而Event_Handler一般而言都是采用絕對(duì)時(shí)間作為記錄超時(shí)的時(shí)間戳,但是絕對(duì)時(shí)間的方式在系統(tǒng)時(shí)鐘被調(diào)整的時(shí)候,會(huì)導(dǎo)致“丟失”部分定時(shí)器的處理,導(dǎo)致一些問題。
    在設(shè)置定時(shí)器時(shí),schedule_timer函數(shù)通過gettimeofday得到定時(shí)器時(shí)間點(diǎn)的時(shí)間。
    template long
    ACE_Select_Reactor_T::schedule_timer
    (ACE_Event_Handler *handler,
    const void *arg,
    const ACE_Time_Value &delay_time,
    const ACE_Time_Value &interval)
    {
    // schedule_timer記錄的是系統(tǒng)時(shí)間,
    if (0 != this->timer_queue_)
    return this->timer_queue_->schedule
    (handler,
    arg,
    timer_queue_->gettimeofday () + delay_time,
    interval);
    }
    在派發(fā)定時(shí)器的過程中也是調(diào)用gettimeofday函數(shù)。
    template ACE_INLINE int
    ACE_Timer_Queue_T::expire (void)
    {
    if (!this->is_empty ())
    return this->expire (this->gettimeofday () + timer_skew_);
    else
    return 0;
    }
    可以看出,如果在schedule_timer后,將系統(tǒng)時(shí)鐘向前調(diào)節(jié)(調(diào)慢)以后,原有的定時(shí)器將要經(jīng)過更多的時(shí)間才能觸發(fā)。從而導(dǎo)致這段時(shí)間內(nèi)定時(shí)器無法觸發(fā)。從而造成定時(shí)器丟失。
    這個(gè)問題的解決方法有2個(gè),簡單方法是將系統(tǒng)時(shí)鐘校準(zhǔn)的頻度提高,保證每次校準(zhǔn)的時(shí)候,系統(tǒng)的時(shí)鐘出現(xiàn)的偏差都不會(huì)影響時(shí)鐘的定時(shí)器觸發(fā)。
    另外一種是ACE的Timer_Queue自己提供的方法,通過上面的代碼我們可以發(fā)現(xiàn),其實(shí)ACE_Timer_Queue_T::gettimeofday是一個(gè)調(diào)用的是一個(gè)函數(shù)指針。默認(rèn)使用ACE_OS:: gettimeofday函數(shù),這個(gè)函數(shù)可以替換的。
    void gettimeofday (ACE_Time_Value (*gettimeofday)(void));
    ACE提供一個(gè)依賴于操作系統(tǒng)的高解析定時(shí)器,ACE_High_Res_Timer,這個(gè)類是通過OS的TICK數(shù)量來得到更加精確的時(shí)鐘的【注】。
    【注】OS在啟動(dòng)后,都會(huì)有一個(gè)TICK在不斷的計(jì)數(shù),這個(gè)TICK就像一個(gè)打點(diǎn)計(jì)數(shù)器,每次增加1.一般計(jì)數(shù)周期就是一個(gè)CPU周期。
    由于CPU的TICK不會(huì)隨著你調(diào)整系統(tǒng)時(shí)鐘而調(diào)整。所以可以看做是一個(gè)相對(duì)值。ACE_High_Res_Timer可以根據(jù)相對(duì)值計(jì)算得到非常精確的程序運(yùn)行時(shí)鐘,。直接使用ACE_High_Res_Timer:: gettimeofday_hr函數(shù)作為ACE_Timer_Queue_T::gettimeofday函數(shù)指針。并且在程序的開始部分使用函數(shù),ACE_High_Res_Timer::global_scale_factor (),用于激活高精度定時(shí)器?!咀ⅰ?BR>    【注】這個(gè)方法得益于原來公司的兩位同事zhangtianhu和liaobincai的一個(gè)終結(jié)。在此懷念一下和他們共事的日子。另外,我沒有仔細(xì)研究過這個(gè)方法,由于獲取CPU的TICK的獲取很有可能是一個(gè)內(nèi)核操作,效率可能不高。
    采用上述的兩個(gè)方法基本可以避免這個(gè)問題。