1. 程式人生 > >定時器分析

定時器分析

定時器分析

文章目錄

時鐘分類

實時時鐘(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()函式來獲取當前的時間和日期.

初略過程:

  1. 初始化xtime變數, 設定xtime 的值為1970.1.1到現在的秒數.
  2. 初始化wall_to_monotonic變數 . 他儲存的是將要加到xtime變數的秒和納秒.
  3. 通過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訊號。