定時器分析
定時器分析
文章目錄
時鐘分類
實時時鐘(RTC): 在電腦斷電後還繼續工作, 所以他有一個外接電池的一個硬體設施. 它是一個16位的計數器, 而linux系統的日期和時間也是開機後根據RTC來獲取的, 然後獲取之後就不再需要RTC來獲取時間了, 而是通過軟體進行時間的維護, 當關機的時候在把時間寫回到RTC中, 而核心通過0x70和0x71埠訪問RTC.
時間戳計數器(TSC) : 它記錄自啟動以來處理器消耗的時鐘週期. 在每個時鐘到來時, 該計數器自動加一. 精準性很高.
可程式設計間隔定時器(PIT) : 它是根據核心設定的固定頻率來發出時鐘中斷, 頻率可以通過編制改編.
CPU本地定時器(APIC) : 能夠產生單步中斷的或週期性中斷的裝置.
jiffies變數
jiffies是個全域性變數, 它是計數電腦自開機以來到現在經過的節拍總數, 一般設定的是32位, 但是也是通過兩個32位擴充套件到64位, 所以也不會擔心溢位問題.
jiffies在開發中初始化的並不是0, 而是0xfffb6c20的值, 這個值大概在5分鐘會溢位, 這樣用於發現有缺陷的程式碼.
xtime變數
xtime
結構是存放了timespec兩個變數,
tv_sec存放自1970.1.1到現在的秒數,
tv_nsec存放自上一秒經過的納秒.
時鐘初始化
核心中time_none
是一個虛擬定時器的物件, cur_timer
指向的是虛擬定時器物件的地址.
在核心初始化的時候, 會呼叫time_init()
函式來獲取當前的時間和日期.
初略過程:
- 初始化xtime變數, 設定xtime 的值為1970.1.1到現在的秒數.
- 初始化wall_to_monotonic變數 . 他儲存的是將要加到xtime變數的秒和納秒.
- 通過select_timer()函式設定
cur_timer()
選擇合適的定時器物件的地址.
時間更新
mark_offset
檢查丟失的時鐘中斷. 時間中斷的可能是中斷被禁止了, 或是其他原因導致時鐘中斷丟失.
全域性時鐘中斷處理程式通過呼叫update_times()
函式來更新xtime
變數.
void update_times(void)
{
unsigned long ticks;
ticks = jiffies - wall_jiffies;
if(ticks)
{
wall_jiffies += ticks;
update_wall_time(ticks);
}
calc_load(ticks);
}
檢查有沒有丟失的中斷, 如果有的話就將丟失的中斷加上在更新xtime變數.
update_times()
的另一個作用是每次節拍都會呼叫calc_load統計處在TASK_TUNNING或TASK_UNINTERRUPTIBLE程序數, 利用這個來更新平均系統負載.
動態定時器
動態定時器是由核心呼叫執行的. 動態定時器在被動的建立和撤銷, 超過時也會被銷燬, 個數也沒有限制.
動態定時器結構是由5個不同大小的vec陣列連結串列構成的, tv1…tv5大小分別是255, 2^14-1, 2^20-1, 2^26-1, 2^32-1. 定時器的執行是從tv1
開始的, 當tv1的255個執行完後, 會從tv2中選擇在新增到tv1中補充, 後面同理的補充前一個tv, tv5不進行補充.
typedef struct tvec_base_c
{
spinlock_t lock;
struct timer_list *running_timer;
unsigned long timer_jiffies;
unsigned long next_timer;
struct tvec_root tv1;
struct tvec tv2;
struct tvec tv3;
struct tvec tv4;
struct tvec tv5;
} tvec_base_t
動態定時器在撤銷的時候有一點麻煩, 因為在撤銷的時候定時程式剛好還在其他CPU中執行, 所以需要設定函式一直等待, 知道定時器函式結束.
nanosleep動態定時器
nanosleep是實現將呼叫程序掛起, 在設定的時間後在被喚醒.
通常在涉及的時候都是設定好掛起時間, 設定task的狀態為TASK_INTERRUPTIBLE
, 然後通過schedule_timeout()
函式進行排程, 在設定的時間間隔之內在通過process_timeout()
函式喚醒該程序.
sleep和nanosleep()的區別
sleep()和nanosleep()兩者都是使程序睡眠一段時間後被喚醒,但是實現卻完全不同。
Linux中並沒有提供系統呼叫sleep(),sleep()是在庫函式中實現的,它是通過呼叫alarm()來設定報警時間,呼叫sigsuspend()將程序掛起在訊號SIGALARM上。
nanosleep()則是Linux中的系統呼叫,它是使用定時器來實現的,該呼叫使呼叫程序睡眠,並往定時器佇列上加入一個timer_list型定時器,time_list結構裡包括喚醒時間以及喚醒後執行的函式,通過nanosleep()加入的定時器的執行函式僅僅完成喚醒當前程序的功能。系統通過一定的機制定時檢查這些佇列(比如通過系統呼叫陷入核心後,從核心返回使用者態前,要檢查當前程序的時間片是否已經耗盡,如果是則呼叫schedule()函式重新排程,該函式中就會檢查定時器佇列,另外慢中斷返回前也會做此檢查),如果定時時間已超過,則執行定時器指定的函式喚醒呼叫程序。當然,由於系統時間片可能丟失,所以nanosleep()精度也不是很高。
另外alarm()也是通過定時器實現的,但是其精度只精確到秒級,另外,它設定的定時器執行函式是在指定時間向當前程序傳送SIGALRM訊號。