ARM Linux系統的時鐘機制

來源:互聯網
上載者:User

1. Linux下有兩類時鐘:

1.1 即時鐘RTC

    它由板上電池驅動的“Real Time Clock”也叫做RTC或者叫CMOS時鐘,硬體時鐘。當作業系統關機的時候,用這個來記錄時間,但是對於啟動並執行系統是不用這個時間的。

1.2 系統時鐘

     “System clock”也叫核心時鐘或者軟體時鐘,是由軟體根據時間中斷來進行計數的,核心時鐘在系統關機的情況下是不存在的,所以,當作業系統啟動的時候,核心時鐘是要讀取RTC時間來進行時間同步.

 

2. 標準計時器

2.1 時鐘滴答計時(jiffies)的幾個基本參數

2.1.1 刻度(clock cycle)的頻率-晶振頻率
       計時器Timer晶體振蕩器在1秒內產生的時鐘脈衝個數就是刻度的頻率, 要注意把這個Timer的刻度頻率與時鐘中斷的頻率區別開來,  Linux用宏CLOCK_TICK_RATE來表示計時器的輸入時鐘脈衝的頻率(比如我的為#define CLOCK_TICK_RATE  1193180),該宏定義在arm/mach-xxx/include/mach/timex.h標頭檔中。

2.1.2 時鐘中斷(clock tick)
      我們知道當計數器減到0值時,它就在IRQ0上產生一次時鐘中斷,也即一次時鐘中斷, 計數器的初始值決定了要過多少刻度才產生一次時鐘中斷,因此也就決定了一次時鐘滴答的時間間隔長度.

2.1.3 時鐘中斷的頻率(HZ)
      即1秒時間內Timer所產生的時鐘中斷次數。確定了時鐘中斷的頻率值後也就可以確定Timer的計數器初值。Linux核心用宏HZ來表示時鐘中斷的頻率,而且在不同的平台上HZ有不同的定義值。對於SPARC、MIPS、ARM和i386等平台HZ的值都是100。該宏在ARM平台上的定義如下(/arch/arm/include/asm/param.h)

2.1.4 計數器的初始值

計數器的初始值由宏LATCH定義在檔案:include/linux/jiffies.h

#define LATCH  ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */

2.1.5 jiffies

       在 Linux 核心中,時間由一個名為 jiffies 的全域變數衡量,該變數標識系統啟動以來經過的滴答數。在最低的層級上,計算滴答數的方式取決於正在啟動並執行特定硬體平台;但是,滴答計數通常在一次中斷期間仍然繼續進行。

       標準計時器 API 作為 Linux 核心的一部分已經有很長一段時間了(自從 Linux 核心的早期版本開始)。儘管它提供的精確性比高精確度計時器要低,但它對於在處理物理裝置時提供錯誤覆蓋的傳統驅動程式逾時來說比較理想。在很多情況下,這些逾時實際上從不觸發,而是被啟動,然後被刪除。

       簡單核心計時器使用計時器輪(timer wheel) 實現。這個主意是由 Finn Arne Gangstad 在 1997 年首次引入的。它不理睬管理大量計時器的問題,而是很好地管理數量合理的計時器 — 典型情況。(原始計時器實現只是按照到期順序將計時器實現雙重連結。儘管在概念上比較簡單,但這種方法是不可伸縮的。)時間輪是一個 buckets 集合,其中每個 bucker 表示將來計時器到期的一個時間塊。這些 buckets 使用基於
5 個 bucket 的對數時間定義。使用 jiffies 作為時間粒紋,定義了幾個組,它們表示將來的到期時段(其中每個組通過一列計時器表示)。計時器插入使用具有 O(1) 複雜度的列表操作發生,到期發生在 O(N) 時間內。計時器到期以串聯的形式出現,其中計時器被從高粒度 buckets 刪除,然後隨著它們的到期時間的下降被插入到低粒度 buckets 中。現在我們查看一下針對這個計時器實現的 API。

 

2.2 計時器 API

       Linux 提供了一個簡單的 API 來構造和管理計時器。它包含一些函數(和助手函數),用於建立、取消和管理計時器。

       計時器通過 timer_list 結構定義,該結構包括實現一個計時器所需的所有資料(其中包括列表指標和在編譯時間配置的可選計時器統計資料)。從使用者角度看,timer_list 包含一個到期時間,一個回呼函數(當/如果計時器到期),以及一個使用者提供的上下文。使用者必須初始化計時器,可以採取幾種方法,最簡單的方法是調用 setup_timer,該函數初始化計時器並設定使用者提供的回呼函數和上下文。或者,使用者可以設定計時器中的這些值(函數和資料)並簡單地調用
init_timer。注意,init_timer 由 setup_timer 內部調用。

void init_timer( struct timer_list *timer );void setup_timer( struct timer_list *timer,                      void (*function)(unsigned long), unsigned long data );

        擁有一個經過初始化的計時器之後,使用者現在需要設定到期時間,這通過調用 mod_timer 來完成。由於使用者通常提供一個未來的到期時間,他們通常在這裡添加 jiffies 來從目前時間位移。使用者也可以通過調用 del_timer 來刪除一個計時器(如果它還沒有到期):

int mod_timer( struct timer_list *timer, unsigned long expires );void del_timer( struct timer_list *timer );

最後,使用者可以通過調用 timer_pending(如果正在等待,將返回 1)來發現計時器是否正在等待(還沒有發出):

int timer_pending( const struct timer_list *timer );

2.3 計時器樣本

我們來檢查一下這些 API 函數的實際運行情況。下面的代碼提供了一個簡單的核心模組,用於展示簡單計時器 API 的核心特點。在 init_module 中,您使用 setup_timer 初始化了一個計時器,然後調用 mod_timer 來啟動它。當計時器到期時,將調用回呼函數 my_timer_callback。最後,當您刪除模組時,計時器刪除(通過 del_timer)發生。(注意來自 del_timer 的返回檢查,它確定計時器是否還在使用。)

#include <linux/kernel.h>#include <linux/module.h>#include <linux/timer.h>MODULE_LICENSE("GPL");static struct timer_list my_timer;void my_timer_callback( unsigned long data ){  printk( "my_timer_callback called (%ld).\n", jiffies );}int init_module( void ){  int ret;  printk("Timer module installing\n");  // my_timer.function, my_timer.data  setup_timer( &my_timer, my_timer_callback, 0 );  printk( "Starting timer to fire in 200ms (%ld)\n", jiffies );  ret = mod_timer( &my_timer, jiffies + msecs_to_jiffies(200) );  if (ret) printk("Error in mod_timer\n");  return 0;}void cleanup_module( void ){  int ret;  ret = del_timer( &my_timer );  if (ret) printk("The timer is still in use...\n");  printk("Timer module uninstalling\n");  return;}

 3. 高精確度計時器(hrtimer)

         高精確度計時器(簡稱 hrtimers)提供一個高精確度的計時器管理架構,這個架構獨立於此前討論過的標準計時器架構,原因是合并這兩個架構太複雜。儘管計時器在 jiffies 粒度上運行,hrtimers 在納秒粒度上運行。

        hrtimer 架構的實現方式與標準計時器 API 不同。hrtimer 不使用 buckets 和串聯操作,而是維護一個按時間排序的計時器資料結構(按時間順序插入計時器,以最小化啟用時的處理)。這個資料結構是一個 “紅-黑” 樹,對於注重效能的應用程式很理想(且恰好作為核心中的一個庫普遍可用)。

        hrtimer 架構作為核心中的一個 API 可用,使用者空間應用程式也可以通過 nanosleep、itimers 和 Portable Operating System Interface (POSIX)-timers interface 使用它。hrtimer 架構被主線化(mainlined)到 2.6.21 核心中。

 

3.1 高精確度計時器 API

       hrtimer API 與傳統 API 有些相似,但它們之間的一些根本差別是它能夠進行額外的時間控制。應該注意的第一點是:時間不是用 jiffies 表示的,而是以一種名為 ktime 的特殊資料類型表示。這種表示方法隱藏了在這個粒度上有效行政時間的一些細節。hrtimer API 正式確認(formalize)了絕對時間和相對時間之間的區別,要求調用者指定類型。 

union ktime {s64tv64;#if BITS_PER_LONG != 64 && !defined(CONFIG_KTIME_SCALAR)struct {# ifdef __BIG_ENDIANs32sec, nsec;# elses32nsec, sec;# endif} tv;#endif};

       與傳統的計時器 API 類似,高精確度計時器通過一個結構表示 — 這裡是 hrtimer。這個結構從使用者角度定義定時器(回呼函數、到期時間等)並包含了管理資訊(其中計時器存在於 “紅-黑” 樹、可選統計資料等中)。  

       定義過程首先通過 hrtimer_init 初始化一個計時器。這個調用包含計時器、時鐘定義和計時器模式(絕對或相對)。使用的時鐘在 ./include/linux/time.h 中定義,表示系統支援的各種時鐘(比如系統時鐘或者單一時鐘,後者只表示從一個起點[比如系統啟動]開始的時間)。計時器被初始化之後,就可以通過 hrtimer_start 啟動。這個調用包含到期時間(在
ktime_t 中)和時間值的模式(絕對或相對值)。

 

struct hrtimer {struct rb_nodenode;ktime_t_expires;ktime_t_softexpires;enum hrtimer_restart(*function)(struct hrtimer *);struct hrtimer_clock_base*base;unsigned longstate;#ifdef CONFIG_TIMER_STATSintstart_pid;void*start_site;charstart_comm[16];#endif};

 

void hrtimer_init( struct hrtimer *time, clockid_t which_clock, enum hrtimer_mode mode );int hrtimer_start(struct hrtimer *timer, ktime_t time, const enum hrtimer_mode mode);

hrtimer 啟動後,可以通過調用 hrtimer_cancel 或 hrtimer_try_to_cancel 來取消。每個函數都包含將被停止的計時器的 hrtimer 引用。這兩個函數的區別在於:hrtimer_cancel 函數試圖取消計時器,但如果計時器已經發出,那麼它將等待回呼函數結束;hrtimer_try_to_cancel 函數也試圖取消計時器,但如果計時器已經發出,它將返回失敗。

int hrtimer_cancel(struct hrtimer *timer);int hrtimer_try_to_cancel(struct hrtimer *timer);

可以通過調用 hrtimer_callback_running 來檢查 hrtimer 是否已經啟用它的回呼函數。注意,這個函數由 hrtimer_try_to_cancel 內部調用,以便在計時器的回呼函數被調用時返回一個錯誤。

int hrtimer_callback_running(struct hrtimer *timer);

3.2 一個 hrtimer 樣本

#include <linux/kernel.h>#include <linux/module.h>#include <linux/hrtimer.h>#include <linux/ktime.h>MODULE_LICENSE("GPL");#define MS_TO_NS(x)(x * 1E6L)static struct hrtimer hr_timer;enum hrtimer_restart my_hrtimer_callback( struct hrtimer *timer ){  printk( "my_hrtimer_callback called (%ld).\n", jiffies );  return HRTIMER_NORESTART;}int init_module( void ){  ktime_t ktime;  unsigned long delay_in_ms = 200L;  printk("HR Timer module installing\n");  ktime = ktime_set( 0, MS_TO_NS(delay_in_ms) );  hrtimer_init( &hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL );    hr_timer.function = &my_hrtimer_callback;  printk( "Starting timer to fire in %ldms (%ld)\n", delay_in_ms, jiffies );  hrtimer_start( &hr_timer, ktime, HRTIMER_MODE_REL );  return 0;}void cleanup_module( void ){  int ret;  ret = hrtimer_cancel( &hr_timer );  if (ret) printk("The timer was still in use...\n");  printk("HR Timer module uninstalling\n");  return;}

關於 hrtimer API,還有許多內容這裡沒有涉及到。一個有趣的方面是它能夠定義回呼函數的執行內容(比如在 softirq 或 hardiirq 上下文中)。您可以在 ./include/linux/hrtimer.h 檔案中進一步瞭解 hrtimer API。

 

 

 

 

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.