歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> ARM Linux系統的時鐘機制

ARM Linux系統的時鐘機制

日期:2017/2/28 15:55:14   编辑:Linux教程

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 內部調用。

  1. void init_timer( struct timer_list *timer );
  2. void setup_timer( struct timer_list *timer,
  3. void (*function)(unsigned long), unsigned long data );

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

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

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

  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 的返回檢查,它確定計時器是否還在使用。)

  1. #include <linux/kernel.h>
  2. #include <linux/module.h>
  3. #include <linux/timer.h>
  4. MODULE_LICENSE("GPL");
  5. static struct timer_list my_timer;
  6. void my_timer_callback( unsigned long data )
  7. {
  8. printk( "my_timer_callback called (%ld).\n", jiffies );
  9. }
  10. int init_module( void )
  11. {
  12. int ret;
  13. printk("Timer module installing\n");
  14. // my_timer.function, my_timer.data
  15. setup_timer( &my_timer, my_timer_callback, 0 );
  16. printk( "Starting timer to fire in 200ms (%ld)\n", jiffies );
  17. ret = mod_timer( &my_timer, jiffies + msecs_to_jiffies(200) );
  18. if (ret) printk("Error in mod_timer\n");
  19. return 0;
  20. }
  21. void cleanup_module( void )
  22. {
  23. int ret;
  24. ret = del_timer( &my_timer );
  25. if (ret) printk("The timer is still in use...\n");
  26. printk("Timer module uninstalling\n");
  27. return;
  28. }
Copyright © Linux教程網 All Rights Reserved