linux系統的時間
通常,作業系統可以使用三種方法來表示系統的當前時間與日期:
①最簡單的一種方法就是直接用乙個64位的計數器來對時鐘滴答進行計數。
②第二種方法就是用乙個32位計數器來對秒進行計數,同時還用乙個32位的輔助計數器對時鐘滴答計數,之子累積到一秒為止。因為232超過136年,因此這種方法直至22世紀都可以讓系統工作得很好。
③第三種方法也是按時鐘滴答進行計數,但是是相對於系統啟動以來的滴答次數,而不是相對於相對於某個確定的外部時刻;當讀外部後備時鐘(如rtc)或使用者輸入實際時間時,根據當前的滴答次數計算系統當前時間。
unix類作業系統通常都採用第三種方法來維護系統的時間與日期。
1 基本概念
首先,有必要明確一些linux核心時鐘驅動中的基本概念。
(1)時鐘週期(clock cycle)的頻率:8253/8254 pit(注:pit 為 programmable interval timer 的簡寫, 在電腦中扮演著計時與計數的功能)的本質就是對由晶體振盪器產生的時鐘週期進行計數,晶體振盪器在1秒時間內產生的時鐘脈衝個數就是時鐘週期的頻率。
linux用巨集clock_tick_rate來表示8254 pit的輸入時鐘脈衝的頻率(在pc機中這個值通常是1193180hz),該巨集定義在include/a**-i386/timex.h標頭檔案中:
#define clock_tick_rate 1193180 /* underlying hz */
(2)時鐘滴答(clock tick):我們知道,當pit通道0的計數器減到0值時,它就在irq0上產生一次時鐘中斷,也即一次時鐘滴答。pit通道0的計數器的初始值決定了要過多少時鐘週期才產生一次時鐘中斷,因此也就決定了一次時鐘滴答的時間間隔長度。
(3)時鐘滴答的頻率(hz):也即1秒時間內pit所產生的時鐘滴答次數。類似地,這個值也是由pit通道0的計數器初值決定的(反過來說,確定了時鐘滴答的頻率值後也就可以確定8254 pit通道0的計數器初值)。linux核心用巨集hz來表示時鐘滴答的頻率,而且在不同的平台上hz有不同的定義值。對於alpha和ia62平台hz的值是1024,對於sparc、mips、arm和i386等平台hz的值都是100。該巨集在i386平台上的定義如下(include/a**-i386/param.h):
#ifndef hz
#define hz 100
#endif
根據hz的值,我們也可以知道一次時鐘滴答的具體時間間隔應該是(1000ms/hz)=10ms。
(4)時鐘滴答的時間間隔:linux用全域性變數tick來表示時鐘滴答的時間間隔長度,該變數定義在kernel/timer.c檔案中,如下:
long tick = (1000000 + hz/2) / hz; /* timer interrupt period */
tick變數的單位是微妙(μs),由於在不同平台上巨集hz的值會有所不同,因此方程式tick=1000000÷hz的結果可能會是個小數,因此將其進行四捨五入成乙個整數,所以linux將tick定義成(1000000+hz/2)/hz,其中被除數表示式中的hz/2的作用就是用來將tick值向上圓整成乙個整型數。
另外,linux還用巨集tick_size來作為tick變數的引用別名(alias),其定義如下(arch/i386/kernel/time.c):
#define tick_size tick
(5)巨集latch:linux用巨集latch來定義要寫到pit通道0的計數器中的值,它表示pit將沒隔多少個時鐘週期產生一次時鐘中斷。顯然latch應該由下列公式計算:
latch=(1秒之內的時鐘週期個數)÷(1秒之內的時鐘中斷次數)=(clock_tick_rate)÷(hz)
類似地,上述公式的結果可能會是個小數,應該對其進行四捨五入。所以,linux將latch定義為(include/linux/timex.h):
/* latch is used in the interval timer and ftape setup. */
#define latch ((clock_tick_rate + hz/2) / hz) /* for divider */
類似地,被除數表示式中的hz/2也是用來將latch向上圓整成乙個整數。
2 表示系統當前時間的核心資料結構
作為一種unix類作業系統,linux核心顯然採用本節一開始所述的第三種方法來表示系統的當前時間。linux核心在表示系統當前時間時用到了三個重要的資料結構:
①全域性變數jiffies:這是乙個32位的無符號整數,用來表示自核心上一次啟動以來的時鐘滴答次數。每發生一次時鐘滴答,核心的時鐘中斷處理函式timer_interrupt()都要將該全域性變數jiffies加1。該變數定義在kernel/timer.c原始檔中,如下所示:
unsigned long volatile jiffies;
c語言限定符volatile表示jiffies是乙個易該變的變數,因此編譯器將使對該變數的訪問從不通過cpu內部cache來進行。
(注:volatile的本意是「易變的」,由於訪問暫存器的速度要快過ram,所以編譯器一般都會作減少訪問外部ram的優化。將變數加上volatile修飾,則編譯器保證對此變數的讀寫操作都不會被優化(肯定執行)。
volatile用在如下的幾個地方:
1、中斷服務程式中修改的供其它程式檢測的變數需要加volatile;
2、多工環境下各任務間共享的標誌應該加volatile;
3、儲存器對映的硬體暫存器通常也要加volatile說明,因為每次對它的讀寫都可能由不同意義;
) ②全域性變數xtime:它是乙個timeval結構型別的變數,用來表示當前時間距unix時間基準1970-01-01 00:00:00的相對秒數值。結構timeval是linux核心表示時間的一種格式(linux核心對時間的表示有多種格式,每種格式都有不同的時間精度),其時間精度是微秒。該結構是核心表示時間時最常用的一種格式,它定義在標頭檔案include/linux/time.h中,如下所示:
struct timeval ;
其中,成員tv_sec表示當前時間距unix時間基準的秒數值,而成員tv_usec則表示一秒之內的微秒值,且1000000>tv_usec>=0。
linux核心通過timeval結構型別的全域性變數xtime來維持當前時間,該變數定義在kernel/timer.c檔案中,如下所示:
/* the current time */
volatile struct timeval xtime __attribute__ ((aligned (16)));
但是,全域性變數xtime所維持的當前時間通常是供使用者來檢索和設定的,而其他核心模組通常很少使用它(其他核心模組用得最多的是jiffies),因此對xtime的更新並不是一項緊迫的任務,所以這一工作通常被延遲到時鐘中斷的底半部分(bottom half)中來進行。由於bottom half的執行時間帶有不確定性,因此為了記住核心上一次更新xtime是什麼時候,linux核心定義了乙個類似於jiffies的全域性變數wall_jiffies,來儲存核心上一次更新xtime時的jiffies值。時鐘中斷的底半部分每一次更新xtime的時侯都會將wall_jiffies更新為當時的jiffies值。全域性變數wall_jiffies定義在kernel/timer.c檔案中:
/* jiffies at the most recent update of wall time */
unsigned long wall_jiffies;
③全域性變數sys_tz:它是乙個timezone結構型別的全域性變數,表示系統當前的時區資訊。結構型別timezone定義在include/linux/time.h標頭檔案中,如下所示:
struct timezone ;
基於上述結構,linux在kernel/time.c檔案中定義了全域性變數sys_tz表示系統當前所處的時區資訊,如下所示:
struct timezone sys_tz;
linux 系統時間
一.系統時間 與 硬體時間 本地時間 utc 時區 或 utc 本地時間 時區 時區東為正,西為負,例如在中國,本地時間都使用北京時間,在linux上顯示就是 cst china standard time,中國標準時,注意美國的中部標準時central standard time也縮寫為cst,與...
linux 硬體時間 系統時間
建了乙個虛擬機器,發現每次重啟後系統時鐘總是跟現有時間相差10多個小時 用date s進行修正之後,再次重啟又出現該問題。於是懷疑跟硬體時鐘有關 用hwclock看了下,發現的確硬體時鐘的時間存在差異。調整硬體時鐘 hwclock set date root localhost date s 200...
linux 系統時間 硬體時間
linux時鐘分為系統時鐘 system clock 和硬體時鐘 real time clock,簡稱rtc 系統時鐘是指當前linux kernel中的時鐘 而硬體時鐘則是主機板上由電池供電的時鐘,硬體時鐘可以在bios中進行設定。當linux啟動時,系統時鐘會去讀取硬體時鐘的設定,然後系統時鐘就...