1. 程式人生 > >深入理解Linux內核 學習筆記(5)

深入理解Linux內核 學習筆記(5)

定時中斷 str spa orm 部分 oid 直接 實時 計數器

第五章 定時測量

內核必須顯式地與三種時鐘打交道:實時時鐘(Real Time Clock, RTC)、時間標記計數器(Time Stamp Counter, TSC)及可編程間隔定時器( ProgrammableIntervalTimer,PIT)。前兩種硬件設備允許內核跟蹤當前的時間;後一種設備由內核編程,以使它能以固定的、預先定義的頻率發出中斷。對於內核和用戶程序使用的定時器來說,這樣的周期性巾斷是至關重要的。

實時時鐘:

所有的PC都包含了一個叫實時時鐘(RTC)的時鐘.它是獨立於CPU和所有其他芯片的。即使當PC關掉電源,RTC還繼續走,因為它靠個小電池或蓄電池供電。RTC能發出周期性的中斷,可作為一個鬧鐘來工作。

Linux只用RTC來獲得時間和日期,然而,通過作用於/dev/rtc設備文件,也允許進程對RTC編程。內核通過0x70和0x71 1/O端口存取RTC。通過執行/sbin/clock系統程序(它直接作用於這兩個I/O端口),系統管理員可以配置時鐘。

時間標記計數器

一個64位的時間標記計數器(TSC)的寄存器,可以通過匯編語言指令rdtsc讀這個寄存器。這個寄存器是一個計數器,它在每個時鐘信號到來時加1,例如,如果時鐘節拍的頻率是400 MHz,那麽,時間標記計數器每2.5納秒增加一次。Linux 利用這個寄存器獲得精準的時間測量

可編程間隔定時器

時間測量設備,到了發定時中斷,PLT以某一固定的頻率(由內核決定)不停地發出中斷,定時中斷的間隔叫做節拍,

時間計數器TSC :

如果CPU有TSC寄存器,用do_ gett imeofday()計算當前時間,否則,用

do_ normal_ gettime()計算。 在do_ _get_ fast_ time變量存放的指針指向合適的函數。

當TSC寄存器可用時,用do_ fast_ gett imecffset ()計算微秒數,否則,用do_ slow_ gect imeof fset ()計算。這個函數的地址存放在do_ gett imeoffset變量中。

在內核啟動時運行的time_ init() 函數能將這些變最指向正確的函數。

進程描述符的counter域表示進程在CPU上運行剩余節拍數。(counter由一個下半部分以延緩的方式更新,一次遞減可能大於一個節拍),變得小於0時,把need_resched置為1,恢復用戶態程序執行時掉schedule,換程序在CPU執行

系統調用函數adjtimex(),每660s調用set_rtc_mmss調整一次實時時鐘。

定時器:

一種軟件工具,每個定時器有一個域,表示需要多長時間到期,jiffies+正確節拍數,每次內核檢查定時器,就用jiffies和這個比較。由於使用下半部分,所以不能嚴格遵守時間(延遲幾百ms)

Linux有三種類型的定時器,靜態定時器,動態定時器和間隔定時器,前兩者有內核使用,第三種可以由進程在用戶態創建。

靜態定時器

存在timer_table數組,每項一個結構體,

struct t imer_ struct (
unsigned long expires;

void (*fr.) lvoid) ;
);

expires域指定定時器到期時間。這個時間被表示成自系統啟動以來的時鐘節拍數。expires值小於或等於jiffies值的所有定時器就認為是到期了或停止了。fn域包含定時器到期時被執行函數的地址。

靜態定時器檢測工作也是由TIMER_BH下半部分調用函數檢查。在內核激活一個定時器時,要在timer_active設置合適的 標誌,這裏會先清0然後執行fn。

動態定時器

struct timer_ list {

struct timer_ list *noxt;

struct tiner_ list *prev;

unsigned long expires;

uns igned long data ;

void (* funct ion) (uns-gned 1ong) ;

};.

function域包含定時器到期時要執行函數的地址。data域指定傳遞給這個定時器函數的參數。

data域使得定義一個通用閑數處理幾個設備驅動程序的定時問題成為可能,可以在data域存放設備ID,或其他有意義的數據,這些數據可被用來區分不同設備的函數使用。

expires域的含義與靜態定時器相應域的含義相同。,

next和prev域實現雙向循環鏈表的連接。事實上,每個活動動態定時器根據expires的值被精確地插人到512個雙向循環鏈表的其中-一個中。在本章稍後將描述使用這個鏈表的算法。

動態定時器的遍歷不像靜態的是單循環,使用了一個聰明的數據結構。這個數據結構叫tvecs的數組,數組的元素指向tv1-5結構標識的鏈表。tv1是struct timer_vec_root,

struct timer_vec_root (
int index;
Struct timer_list *vec[TVR_SIZE] ;
);

其TVR_SIZE為256,index指定當前掃描的鏈表,每個節拍時增1,模256為0時,由tv2-tv5順序補充。每個vec指向一個timer_ list鏈表。

tv2-tv4這個TVR_SIZE為64,炎型為struct timer_vec的tv2、tv3及tv4結枸分別包含了在緊接著到來的214-1、220-1及226-1個節拍內將要到期的所有動態定吋器。

tv5的結構與前面的幾個結構相同,除了vec數組的最後一項包含的動態定時器expires域的值可以任意大,它再也不需要從另一個數組進行補充了。

動態定時器的應用:在一些情況下,例如,當內核不能提供一個給定的服務時,就可以把當前進程掛起一個固定的時間。這通常是通過執行一個進程延時完成的。

與定時測量相關的系統調用:

Gettimeofday():該函數實現了time()和ftime(),root用戶可以調用stime或settimeofday來修改當前日期和時間,不過請註意當這兩個系統調用修改xtime的值時都沒有修改RTC寄存器,因此當系統關機時新的時間會丟失,除非用戶執行/sbin/clock這個程序來改變RTC的值。

Settimer和alarm:間隔定時器,

通過POSIX setit imer ()系統調用可以激活間隔定時器。第一個參數指定應當采取下面的哪一個策略:

ITIMER_REAL:真正過去的時間;進程接受SIGALRM信號
ITIMER_VIRTUAL:進程在用戶念下花費的時間;進程接受SIGVTALRM信號
ITIMER_PROF:進程既在用戶態下又在內核態下所花費的時間;進程接受SIGPROF信號
為了能分別實現前述每種策略的間隔定時器,進程描述符要包含三對域:
it_real_incr 和it_real_value
it_virt_incr 和it_virt_value
it_prof_incr 和it_prof_value

每對中的第一個域存放著兩個信號之間以節拍為單位的問隔;另一個城存放著定時器的當前值。

ITIMER_REAL間隔定時器是利用動態定時器實現的,因為即使進程不在CPU上運行時,內核也必須向進程發送信號。因此,每個進程描述符包含一個叫real_ timer的動態定時器對象。setitimer(}系統調用初始化rea1_timer域,然後調用add_timer()把動態定時器插人到合適的鏈表中。當定時器到期時,內核執行it_rea1_fn()定時函數。依次類推,it_real_in() 函數向進程發送一個SIGALRM信號。如果it_real_incr不為空,那麽它會再次設置expires城,重新激活定時器。

ITIMER_VIRTUAL和ITIMER_PROF間隔定時器不需要動態定時器,因為只有當進程運行時,它們才能被更新: do_it_virt()和dc_it_prof () 由update_ore_process ()調用,當執行TIMER_BH下半部分時update_one_process()才運行。因此,每個節拍中,這兩個間隔定時器通常都被更新--次,並且如果它們到期,就給當前進程發送一個合適的信號。

a1arm()系統調用會在一個指定的時間間隔用完時向調用的進程發送一個SIGALRM信號。當以ITIMER_ REAL為參數調用時,它非常類似於setitimer(),因為它利用了包含在進程描述符中的real_timer動態定時器。因此,不能同時使用以ITIMER_REAL為參數的alarm()和setitimer()。

深入理解Linux內核 學習筆記(5)