Linux核心系統定時器TIMER實現過程分析

2021-06-02 14:34:38 字數 4586 閱讀 5962

,謝謝!

linux系統定時器,在核心中扮演著重要角色。核心的許多重要實現如任務排程,工作佇列等均以系統定時器關係密切。系統定時器能以可程式設計的頻率中斷處理,這一中斷叫做軟中斷。此頻率即為每秒的定時器節拍數hz。hz的越大,說明定時器節拍越小,執行緒排程的準確性會越高。但hz設得過大,對乙個系統來說並不好,會導cpu開銷過大,反而造成任務排程效率降低。滴答jiffies 變數記錄系統啟動以來,系統定時器已經觸發的次數。也就是說每過一秒jiffies的增量為hz,一般hz=100,hz是可以配置的,在s3c2440 arm linux中配置為200.

下面基於linux2.6.30.4原始碼來**其實現原理及其過程。

要理解系統定時器實現原理,先來看看關係到系統定時器的各種資料結構,其具體的描述引數。

結構體structtimer_list來描述timer的引數,其資料結構如下:

struct timer_list ;

其中:

list_entry結構:

struct list_head ;

tevc_base的結構:

struct tvec_base ____cacheline_aligned;

#define tvn_bits (config_base_small ? 4 :6)

#define tvr_bits (config_base_small ? 6 :8)

#define tvn_size (1 << tvn_bits)

#define tvr_size (1 << tvr_bits)

#define tvn_mask (tvn_size - 1)

#define tvr_mask (tvr_size - 1)

struct tvec ;

struct tvec_root ;

可見涉及到系統定時器的資料結構並不多,那麼:對於乙個linux系統中,定時器個數可能會很多,而且每個定時器的超時事件時間並不相同,所以如何管理和處理定時器超時事件,關係到核心效能的高低。它根據不同的定時事件,按時間間分組,將新增的timer定時器建成雙向鍊錶,然後按照一定方式存放於5組tv1~tv5變數中稱為tec_base。對於對稱式多理器(smp)系統還考慮到了timer從乙個cpu遷移到另乙個cpu的情況,相應的tev_base也跟著更改。那它在系統是怎樣實現的呢?現在先從乙個簡單的系統定時器應用例子來看看它的實現過程:

#include #include #include struct  timer_list   my_timer;

static void my_function(unsigned long data)

static int my_timer_init(void)

static void my_timer_exit(void)

module_init(my_timer_init);

module_exit(my_timer_exit);

module_author( "itspy");

module_license( "gpl");

module_description("linux kernel timerprogramming");

上面例子,實現了乙個定時器事件,將在3 hz(秒)發生。my_imer_init函式中呼叫到的定時器api只有:

init_timer(&my_timer);   //用於定時器初始化

add_timer(&my_timer);    //增加乙個新的定時器到tev_base向量表中

其中init_timer中呼叫了__init_timer(),這個函式才是真正初始化定時器的:

static void __init_timer(struct timer_list*timer,

const char *name,

struct lock_class_key *key)

新增的定時器初始化,就是完成了乙個timer_list結構初始化過程。

add_timer()  --> mod_timer()  -->  __mod_timer()

其中:

static inline int

__mod_timer(struct timer_list *timer,unsigned long expires, bool pending_only)

else …

new_base= __get_cpu_var(tvec_bases); //獲取本地cpu中的tevc_bases

if(base != new_base)

}timer->expires= expires;

internal_add_timer(base,timer); //分析timer expires及建表過程

out_unlock:

spin_unlock_irqrestore(&base->lock,flags);

return ret;

}

對於新增的timer最後呼叫internal_add_timer(base, timer)加入到相應的timer_list中以完成初始化過程。,這是乙個建表的過程,表的建立情況,關係到表的管理效率。之前我們說到它共有tv1~tv5 組,tv1是乙個很特別的組。每個tv中有各個組員,每個timer是如何新增的呢,看看internal_add_timer()的實現過程:

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

else if (idx < 1 << (tvr_bits + tvn_bits)) else if (idx < 1 << (tvr_bits + 2 * tvn_bits)) else if (idx < 1 << (tvr_bits + 3 * tvn_bits)) else if ((signed long) idx < 0) else

i= (expires >> (tvr_bits + 3 * tvn_bits)) & tvn_mask;

vec= base->tv5.vec + i;}/*

* timers are fifo:

*/list_add_tail(&timer->entry,vec);

}

通過**我們知道它的過程是這樣的:首先它根據每個timer中的超時差值idx來決定timer所處的tev_base組別tv1~tv5.所以超時事件越後發生,那麼它所處的組位置越靠後。對於tv1組,超時插值idx為0~255之間。差值idx即為所屬組tv1中的陣列下標。從中可知tv1組中相鄰定時器的超時事件間隔1 jiffies發生。對於tv2組,超時差值idx為 256~2^14(16386)  之間,組中相鄰定時時器超事件時間隔256^1 = 256 jiffies發生。以此類推,tv3 組超時差值idx為(16387~2^20)之間,組中相鄰定時器超時時間間隔256^2 = 65536 jiffies發生 … 最後,新增的timer加入到當前節點(超時差值相等)的尾部list_add_tail()形成乙個雙向鍊錶。這樣分組timer雙向鍊錶方便了後面對定時器的遷移更新管理過程,以及最終提高了cpu的處理效率,因為在__run_timers()時,我們只需掃瞄tv1組中即將到來的定時器事件就行了。

我們知道啟動過程時start_kernel()對定時器的初始化是這樣的 :

init_timers() -> run_timer_softirq()  -> __run_timers()…

timer_interrupt() -> update_process_times() ->run_local_timers() -> raise_softirq(timer_softirq);

之前我寫的一篇《核心窺秘之一:start_kernel()執行過程記錄》也有提到過.

__run_timers()是系統定時器超時事件的服務程式。這是run_timer_softirq()中一部分,是通過軟中斷的實現的,它是在軟中斷下半部處理的。

static inline void __run_timers(structtvec_base *base)}…

}

對於cascade()函式它是確保之前定時器建立時internal_add_timer()定時器佇列以及佇列租得遷移更新工作,為什麼要遷移,因為,系統在處理定時器時,比較的只是tv1組而已,也就是說,原來的tv1執行完之後,那麼剩下的tv2,tv3,tv4,tv5將會先後遷移到tv1組:tv5 -> tv4 -> tv3 -> tv2-> tv1,這樣定時器超時事件服務程式並不需要對每組的tv的超時事件進行檢測,相比而言,也就提高了cpu的處理效率。那麼這樣一來timer 鍊錶將發生變化,所以需要重新計算,重新實現internal_add_timer(),所以cascade ()函式**如下:

tatic int cascade(struct tvec_base *base,struct tvec *tv, int index)

return index;

}

通過以上分析,我們對linux中系統定時器timer實現過程有所了解了。

TIM基本定時器 定時

定時 輸出比較 輸入捕獲 互補輸出 分類 基本定時器 定時 通用定時器 定時 輸出比較 輸入捕獲 高階定時器 定時 輸出比較 輸入捕獲 互補輸出 f103系列的開發板一般是倆個高階定時器tim1 tim8 4個通用定時器tim2 3 4 5 2個基本定時器tim6 7 而根據容量的大小,又稍有區別。...

linux 核心 核心定時器

一.時鐘中斷概念 1.時鐘中斷由系統的定時硬體以週期性的時間間隔產生,這個間隔 即頻率 由核心根據hz來確定,hz是乙個與體系結構無關的常數,可配置 50 1200 在x86平台上預設值是1000 2.每當時鐘中斷發生的時候,全域性變數jiffies unsigned long 就加1,所以jiff...

linux核心定時器

度量時間差 時鐘中斷由系統的定時硬體以週期性的時間間隔產生,這個間隔 頻率 由核心根據hz來確定,hz是乙個與體系結構無關的常數,可配置 50 1200 在x86平台,預設值為1000.每秒鐘產生1000次時鐘中斷 每當時鐘中斷發生時,全域性變數jiffies就加1,因此其記錄了自linux啟動後時...