linux時鐘RTC,TSC,PIT,jiffies
1 基本概念
定時機制連同一些更可見的核心活動(如檢查超時)來驅使程序切換。
兩種主要的定時測量:
- 儲存當前的時間和日期,以便能通過time(), ftime()和gettimeofday()系統呼叫把它們返回給使用者程式。
- 維持定時器,這種機制能夠告訴核心或使用者程式某一時間間隔已經過去了。
定時測量是由基於固定頻率振盪器和計數器的幾個硬體電路完成的。
2 時鐘和定時器電路
時鐘電路用於跟蹤當前時間和產生精確的時間度量。
定時器電路由核心程式設計,所以它們以udingde,預先定義的頻率發出中斷。
時鐘電路的分類
- 用於跟蹤當前時間
- 實時時鐘RTC
- 時間戳計數器TSC
- 產生週期性的時鐘中斷,用於計時
- 可程式設計間隔定時器PIT
2.1 實時時鐘RTC——IRQ8上產生中斷
當PC被切斷電源,RTC還繼續工作。
核心通過0x70和0x71I/O埠訪問RTC。
能在IRQ8上發出週期性的中斷,頻率在2HZ~8192HZ之間,可程式設計
2.2 時間戳計數器TSC
在80x86微處理器中,有一個CLK輸入引線接收外部振盪器的時鐘訊號。TSC在每個時鐘訊號到來時加1.
TSC是一個64位的時間戳計數器暫存器,彙編指令rdtsc讀這個暫存器。Linux在初始化時系統時必須確定時鐘訊號的頻率。
獲得tsc的時鐘頻率:calibrate_tsc()函式通過計算一個大約在5ms的時間間隔內所產生的時鐘訊號的個數來算出CPU實際頻率。
Linux通過rdtscll()或rdtscl()用來讀取TSC的事。
與可程式設計間隔定時器相比,TSC可以獲得更精確的時鐘。
2.3 可程式設計間隔定時器PIT
使用I/O埠0x40~0x43
LInux給PC的第一個PIT進行程式設計,使它以大於1000Hz的頻率向IRQ0發出時鐘中斷,即每1ms產生一次時鐘中斷,這個時間間隔叫做一個節拍(tick),它的長度以納秒為單位存放在tick_nsec變數中。
由setup_pit_timer()進行初始化。在init_pit_timer()中初始化時鐘中斷頻率。
與系統時鐘訊號有關的巨集定義:
(1)巨集定義Hz
在不同的體系機構下,系統時鐘所要求的可程式設計定時器中斷的頻率,即每秒tick的個數
(2)巨集定義CLOCK_TICK_RATE
記錄了不同體系結構下,驅動可程式設計定時器工作的輸入時鐘頻率
(3)巨集定義LATCH
記錄了上述兩個巨集定義的比值,用於在核心初始化過程中設定可程式設計定時器中計數器暫存器counter的初始值。
3 Linux計時體系結構
LInux的計時體系結構是一組與時間流相關的核心資料結構和函式。
功能:
- 更新自系統啟動以來所經過的時間
- 更新時間和日期
- 確定當前程序的執行時間,考慮是否要搶佔
- 更新資源使用統計計數
- 檢查到期的軟定時器
核心有兩個基本的計時函式:
- 保持當前最新的時間
- 計算在當前秒內走過的納秒數
在單處理器系統中,所有定時活動都由IRQ0上的時鐘中斷觸發,包括:
- 在中斷中立即執行的部分
- 作為下半部分延遲執行的部分
3.1 計時體系結構的資料結構
3.1.1定時器物件(時鐘源)
為了使用一種統一的方法來處理可能存在的定時器資源,核心使用能夠了“定時器物件”,它是timer_opts型別的一個描述符。其中最重要的兩個方法:
mark_offset:由時鐘中斷處理程式呼叫,並以適當的資料結構記錄每個節拍到來時的準確時間。
get_offset:使用已記錄的值來計算上一次時鐘中斷(節拍)以來經過的時間。
這兩種方法,使得Linuxd計時體系結構能夠打到子節拍的解析度,也就是說,核心能夠以比節拍週期更高的精度來測定當前的時間,這種操作被稱為“定時插補”。在核心初始化期間,select_timer()函式設定cur_timer指向適當定時器物件(時鐘源)的地址。變數timer_cur存放了某個定時器對應的那個的地址,該定時器是系統可利用的定時器資源中最好的。
3.1.2jiffies變數
一個計數器,用來記錄自系統啟動以來產生的節拍總數。
因為一秒鐘內產生系統時鐘中斷次數等於巨集定義HZ的值,所以變數jiffies的值在一秒內增加HZ。
3.1.3xtime變數
xtime變數存放當前時間和日期,它是一個timespec型別的資料結構。以便核心對某些物件和事件作時間標記,如記錄檔案的建立時間、修改時間、上次訪問時間,或者供使用者程序通過系統呼叫來使用。
基本每個tick更新一次。
3.2 單處理器系統上的計時體系結構
考點:tick_handle_periodic函式的功能(Linux的計時體系結構的功能)
tick_init呼叫clockevents_tegister_notifier註冊tick_notifier到clockevents_chain上。
Update_wall_time()完成變數xtime的更新。
time_init_hook()來設定系統時鐘中斷處理程式。
在時鐘中斷處理函式中:
會呼叫tick_init函式,書上很多流程中的函式最終都是被這個函式所呼叫,流程如下:
4 軟定時器和延遲函式
軟定時器:
- 動態定時器(核心)
- 間隔定時器(可以使用者)
動態定時器:被動態的建立和撤銷,當前活動的動態定時器個數沒有限制
定時器是一種軟體功能,即允許在將來的某個時刻,函式在給定的時間間隔用完時被呼叫。每個定時器都包含一個欄位,表示定時器將需要多長時間才到期。這個欄位的初值就是jiffies的當前值加上合適的節拍數。
注意,對於必須嚴格遵守定時時間的那些實時應用而言,定時器並不適合,因為定時器的檢查總是由可延遲函式進行。
4.1建立並激活一個動態定時器——init_timer初始化一個time_list物件
- 建立一個新的timer_list物件
- 呼叫init_timer初始化,並設定定時器要處理的函式和引數
- 設定定時時間
- 使用add_timer加入到合適的連結串列中
4.2動態定時器的資料結構
用於和系統核心變數jiffies進行比較。- 成員變數function:該函式指標變數儲存了核心定時器超時後要執行的函式,即定時器超時處理函式。
- 成員變數data:該無符號長整型變數用作定時器超時處理函式的引數。
- 成員變數base:該指標變量表明瞭該核心定時器節點歸屬於系統中哪一個處理器,在使用函式init_timer()初始化核心定時器節點的過程中,將該指標指向了一個每處理器變數tvec_bases的成員變數t_base。
4.3動態定時器的維護
run_timer的主要功能
- 定時器時間表示引數加一
- 處理的定時器去除
- 依次處理到期定時器
動態定時器應用之delayed work
動態定時器應用之schedule_timeout: setup_time_on_stack(&timer, process_timeout, (unsigned long)current); timer時間到了之後,process_timeout函式將當前程序變為等待態。
4.4延遲函式:
當核心需要等待一個較短的時間間隔,如幾毫秒,通常裝置驅動器會等待預先定義的整個微秒直到硬體完成某些操作。這些情況下,核心使用udelay()和ndelay()函式:前者接收一個微秒級的時間間隔作為它的引數,並在指定的延遲結束後返回,後者與前者類似,但是指定延遲的引數是納秒級的。