linux時間子系統(五)

3 定時器

  Linux中定時器分兩種,一種是timeout類型,另外一種是timer類型。timeout類型的定時器一般用於檢測各類錯誤條件,例如用於檢測網卡發收數據包是否會超時,IO設備的讀寫是否會超時的定時器等。使用timeout類型的定時器每每不關心超時處理,所以超時精確與否,並不重要。這類定時器是基於time wheel機制實現的。timer類型的定時器與timeout類型的定時器正好相反,使用timer類型的定時器每每要求在精確的時鐘條件下完成特定的事件。timer類型的定時器是基於紅黑樹實現的。數組

  Linux須要進行時鐘管理,離不開底層的硬件支持。在早期的Linux內核中,經過8253芯片提供的PIT來提供時鐘,可是PIT的頻率很低,只能提供最多1ms的時鐘精度,因爲PIT觸發的中斷速度太慢,會致使很大的時延,對於像音視頻這類對時間精度要求很高的應用並不足夠,會極大的影響用戶體驗。隨着硬件平臺的不斷髮展,陸續出現了TSC,HPET,ACPI PM Timer,CPU Local APIC Timer等精度更高的時鐘,內核爲了可使用更高精度的定時器,開發出了基於rbtree的hrtimer子系統。app

3.1 time wheel 

  在Linux 2.6.16以前,內核一導致用一種被稱爲time wheel的機制來管理定時器。這就是內核一直採用的基於HZ的定時器機制。spa

  爲了不競爭,內核爲每一個cpu定義了一個tvec_base結構指針,用來保存定時器。指針

3.1.1 tvec_base結構

struct tvec_base {視頻

        spinlock_t lock;索引

        struct timer_list *running_timer;事件

        wait_queue_head_t wait_for_running_timer;內存

        unsigned long timer_jiffies;開發

        unsigned long next_timer;it

        struct tvec_root tv1;

        struct tvec tv2;      

        struct tvec tv3;

        struct tvec tv4;

        struct tvec tv5;

} ____cacheline_aligned;  

  running_timer,該字段指向當前cpu正在處理的定時器所對應的timer_list結構。

  timer_jiffies,該字段表示當前CPU定時器所經歷過的jiffies數。大多數狀況下,該數和jiffies計數值相等,若是cpu的idle狀態連續持續了多個jiffies時,當退出idle狀態時,jiffies計數值就會大於該字段,在接下來的tick中斷後,定時器系統會讓該字段等於jiffies值。

  next_timer,該字段指向該CPU下一個即將到期的定時器。

  tv1 -- tv5,這5個字段用於對定時器進行分組。實際上,tv1-tv5都是一個鏈表數組,其中tv1的數組大小爲TVR_SIZE,tv2-tv5的大小爲TVN_SIZE,根據CONFIG_BASE_SMALL配置項不一樣,他們有不一樣的大小。默認狀況下,CONFIG_BASE_SMALL未使能,TVR_SIZE=256,TVN_SIZE=64。若系統內存不足,則可使能CONFIG_BASE_SMALL,此時TVR_SIZE=64,TVN_SIZE=16。

3.1.2 time wheel機制

  time wheel機制的工做原理相似於水錶的工做原理。假定沒有使能CONFIG_BASE_SMALL,此時tv1-tv5這5個鏈表數組的大小分別是256,64,64,64,64。因爲tv1中的定時器會被最早處理而tv5中的定時器會被最後處理,咱們能夠認爲tv1-tv5分別佔據一個32位數的不一樣比特位,其中tv1佔據最低的8位,tv2佔據緊接着的6爲,tv5佔據最後的6位。

  當註冊一個定時器時,咱們能夠獲取定時器到期時間和所屬cpu的tvec_base結構中timer_jiffies字段的差值,記爲idx。以後比較idx與1<<8-1,1<<14-1, 1<<20-1, 1<<26-1, 1<<32-1的值,肯定定時器應該存放的鏈表數組。假設idx=4,則存放到tv1數組中。假定idx=500,則存放到tv2數組中。

  當肯定了鏈表數組後,接着要肯定把該定時器放入數組的哪個鏈表中。若是idx的值小於256,則要被放入tv1中,因此能夠簡單的使用定時器到期時間timer_list.expires的低8位做爲數組下標索引,放入tv1相應的鏈表中便可。若是idx的值在256-16383之間,則須要把定時器放入tv2鏈表數組中,因此可使用定時器到期時間timer_list.expires的8-14位做爲數組的下標索引便可。tv3-tv5同理,即放入(timer_list.expires << (TVN_SIZE + n*TVR_SIZE)) & (n?TVR_MASK:TVN_MASK)做爲下標索引的相應鏈表便可。

    static void internal_add_timer(struct tvec_base *base, struct timer_list *timer)

{

        unsigned long expires = timer->expires;

        unsigned long idx = expires - base->timer_jiffies;

        struct list_head *vec;

 

        if (idx < TVR_SIZE) {

                int i = expires & TVR_MASK;

                vec = base->tv1.vec + i;

        } else if (idx < 1 << (TVR_BITS + TVN_BITS)) {

                int i = (expires >> TVR_BITS) & TVN_MASK;

                vec = base->tv2.vec + i;

        } else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) {

                int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;

                vec = base->tv3.vec + i;

        } else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {

                int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;

                vec = base->tv4.vec + i;

        } else if ((signed long) idx < 0) {

                /*

                 * Can happen if you add a timer with expires == jiffies,

                 * or you set a timer to go off in the past

                 */

                vec = base->tv1.vec + (base->timer_jiffies & TVR_MASK);

        } else {

                int i;

                /* If the timeout is larger than 0xffffffff on 64-bit

                 * architectures then we use the maximum timeout:

                 */

                if (idx > 0xffffffffUL) {

                        idx = 0xffffffffUL;

                        expires = idx + base->timer_jiffies;

                }

                i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;

                vec = base->tv5.vec + i;

        }

        /*

         * Timers are FIFO:

         */

        list_add_tail(&timer->entry, vec);

}

   定時器的添加,就是首先計算定時器與所屬cpu的tvec_base->timer_jiffies的差值,再根據idx的值和定時器的到期時間將定時器放入tv1-tv5鏈表數組的某一鏈表中。

相關文章
相關標籤/搜索