1. 程式人生 > >【Linux作業系統分析】定時測量——RTC,TSC,PIT,jiffies,計時體系結構,延遲函式

【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()函式:前者接收一個微秒級的時間間隔作為它的引數,並在指定的延遲結束後返回,後者與前者類似,但是指定延遲的引數是納秒級的。