jiffies溢位與時間先後比較

2021-08-28 10:08:48 字數 4636 閱讀 3728

**:

1. 概述

在linux核心中,tcp/ip協議棧在很多用到時間比較的地方都使用了jiffies?本文介紹了什麼是jiffies,jiffies溢位可能造成的問題,使用time_after等巨集來正確地比較時間及其背後的原理。

2. jiffies簡介

2.1 時鐘中斷

在linux核心中,tcp/ip協議棧在很多用到時間比較的地方都使用了jiffies。

那麼jiffies是什麼呢?我們知道,作業系統應該能夠在將來某個時刻準時排程某個任務,所以需要一種能保證任務準時排程執行的機制。希望支援每種作業系統的微處理器必須包含乙個可週期性中斷它的可程式設計間隔定時器。這個週期性中斷被稱為系統時鐘滴答(或system timer),它象節拍器一樣來組織系統任務,也稱為時鐘中斷。

時鐘中斷的發生頻率設定為hz,hz是乙個與體系結構無關的常數,在檔案中定義。至少從2.0版到 2.1.43版,alpha平台上linux定義hz的值為1024,而其他平台上定義為100。時鐘中斷對作業系統是非常重要的,當時鐘中斷發生時,將周期性地執行一些功能,例如:

. 更新系統的uptime

. 更新time of day

. 檢查當前任務的時間片是否用光,如果是則需要重新排程任務

. 執行到期的dynamic timer

. 更新系統資源使用統計和任務所用的時間統計

2.2 jiffies及其溢位

全域性變數jiffies取值為自作業系統啟動以來的時鐘滴答的數目,在標頭檔案中定義,資料型別為 unsigned long volatile (32位無符號長整型)。關於jiffies為什麼要採用volatile來限定,可參考《關於volatile和jiffies.txt》。

jiffies轉換為秒可採用公式:(jiffies/hz)計算,將秒轉換為jiffies可採用公式:(seconds*hz)計算。

當時鐘中斷發生時,jiffies值就加1。因此連續累加一年又四個多月後就會溢位(假定hz=100,1個jiffies等於1/100 秒,jiffies可記錄的最大秒數為(2^32 -1)/100=42949672.95秒,約合497天或1.38年),即當取值到達最大值時繼續加1,就變為了0。

在vxworks作業系統中,定義hz的值為60,因此連續累加兩年又三個多月後也將溢位(jiffies可記錄的最大秒數為約合2.27 年)。如果在vxworks作業系統上的應用程式對jiffies的溢位沒有加以充分考慮,那麼在連續執行兩年又三個多月後,這些應用程式還能夠穩定執行嗎?

下面我們來考慮jiffies的溢位,我們將從以下幾個方面來闡述:

. 無符號整型溢位的具體過程

. jiffies溢位造成程式邏輯出錯

. linux核心如何來防止jiffies溢位

. time_after等比較時間先/後的巨集背後的原理

. **中使用time_after等比較時間先/後的巨集

3. 無符號整型溢位的具體過程

我們首先來看看無符號長整型(unsigned long)溢位的具體過程。實際上,無符號整型的溢位過程都很類似。為了更具體地描述無符號長整型溢位的過程,我們以8位無符號整型為例來加以說明。

8位無符號整型從0開始持續增長,當增長到最大值255時,繼續加1將變為0,然後該過程周而復始:

0, 1, 2, ..., 253, 254, 255,

0, 1, 2, ..., 253, 254, 255,

...4. jiffies溢位造成程式邏輯出錯

下面,通過乙個例子來看jiffies的溢位。

例4-1:jiffies溢位造成程式邏輯出錯

unsigned long timeout = jiffies + hz/2; /* timeout in 0.5s */

/* do some work ... */

do_somework();

/* then see whether we took too long */

if (timeout > jiffies) else

本例的目的是從當前時間起,如果在0.5秒內執行完do_somework(),則呼叫no_timeout_handler()。如果在0.5秒後執行完do_somework(),則呼叫timeout_handler()。

我們來看看本例中一種可能的溢位情況,即在設定timeout並執行do_somework()後,jiffies值溢位,取值為0。設在設定 timeout後,timeout的值臨近無符號長整型的最大值,即小於2^32-1。設執行do_somework()花費了1秒,那麼**應當呼叫 timeout_handler()。但是當jiffies值溢位取值為0後,條件timeout > jiffies成立,jiffies值(等於0)小於timeout(臨近但小於2^32-1),儘管從邏輯上講jiffies值要比timeout大。但最終**呼叫的是no_timeout_handler(),而不是timeout_handler()。

6. time_after等比較時間先後的巨集背後的原理

那麼,上述time_after等比較時間先/後的巨集為什麼能夠解決jiffies溢位造成的錯誤情況呢?

我們仍然以8位無符號整型(unsigned char)為例來加以說明。仿照上面的time_after巨集,我們可以給出簡化的8位無符號整型對應的after巨集:

#define uc_after(a, b) ((char)(b) - (char)(a) < 0)

設a和b的資料型別為unsigned char,b為臨近8位無符號整型最大值附近的乙個固定值254,下面給出隨著a(設其初始值為254)變化而得到的計算值:

a     b     (char)(b) - (char)(a)

254     254         0

255             - 1

0             - 2

1             - 3

...124             -126

125             -127

126             -128

127             127

128             126

...252             2

253             1

從上面的計算可以看出,設定b不變,隨著a(設其初始值為254)不斷增長1,a的取值變化為:

254, 255, (一次產生溢位)

0, 1, ..., 124, 125, 126, 127, 126, ..., 253, 254, 255, (二次產生溢位)

0, 1, ...

...而(char)(b) - (char)(a)的變化為:

0, -1,

-2, -3, ..., -126, -127, -128, 127, 126, ..., 1, 0, -1,

-2, -3, ...

...從上面的詳細過程可以看出,當a取值為254,255, 接著在(一次產生溢位)之後變為0,然後增長到127之前,uc_after(a,b)的結果都顯示a是在b之後,這也與我們的預期相符。但在a取值為 127之後,uc_after(a,b)的結果卻顯示a是在b之前。

從上面的運算過程可以得出以下結論:

使用uc_after(a,b)巨集來計算兩個8位無符號整型a和b之間的大小(或先/後,before/after),那麼a和b的取值應當滿足以下限定條件:

. 兩個值之間相差從邏輯值來講應小於有符號整型的最大值。

. 對於8位無符號整型,兩個值之間相差從邏輯值來講應小於128。

從上面可以類推出以下結論:

對於time_after等比較jiffies先/後的巨集,兩個值的取值應當滿足以下限定條件:

兩個值之間相差從邏輯值來講應小於有符號整型的最大值。

對於32位無符號整型,兩個值之間相差從邏輯值來講應小於2147483647。

對於hz=100,那麼兩個時間值之間相差不應當超過2147483647/100秒 = 0.69年 = 248.5天。對於hz=60,那麼兩個時間值之間相差不應當超過2147483647/60秒 = 1.135年。在實際**應用中,需要比較先/後的兩個時間值之間一般都相差很小,範圍大致在1秒~1天左右,所以以上time_after等比較時間先 /後的巨集完全可以放心地用於實際的**中。

7. **中使用time_after等比較時間先/後的巨集

下面**是針對例4-1修改後的正確**:

例7-1:在例4-1基礎上使用time_before巨集後的正確**

unsigned long timeout = jiffies + hz/2; /* timeout in 0.5s */

/* do some work ... */

do_somework();

/* then see whether we took too long */

if (time_before(jiffies, timeout)) else

8. 結論

系統中採用jiffies來計算時間,但由於jiffies溢位可能造成時間比較的錯誤,因而強烈建議在編碼中使用time_after等巨集來比較時間先後關係,這些巨集可以放心使用。

9. 參考資料

[1] 在《linux kernel development, second edition》chapter 10中給出了與時間相關的hz、jiffies、延遲、各種timer等更全面,更精彩而有趣的討論。

[2]《關於volatile和jiffies》,可見

jiffies溢位與時間先後比較

title jiffies溢位與時間先後比較 編制 chinakapok sina.com 日期 2005 05 25 1.概述 在linux核心中,tcp ip協議棧在很多用到時間比較的地方都使用了jiffies?本文介紹了什麼是jiffies,jiffies溢位可能造成的問題,使用time af...

php mysql 溢位 mysql時間戳溢位問題

眾所周知,時間戳是自 1970 年 1 月 1 日 00 00 00 gmt 以來的秒數。它也被稱為 unix 時間戳 unix timestamp mysql也提供了時間戳方面的函式,如unix timestamp 和from unixtime 現在通用的時間戳貌是32位無符號整形,這也就標示著時...

timeval時間溢位問題

該結構體是linux系統中定義,struct timeval結構體在time.h中的定義為 struct timeval 在ndk中使用tv sec或者tv usec時,需先強制型別轉換,否則直接用於計算或者顯示會有溢位現象 如tv sec的值輸出為負數 例子如下 long long getcurr...