jiffies相關時間比較函數time_after、time_before詳解


 

1. jiffies簡介

  首先,操作系統有個系統專用定時器(system timer),俗稱滴答定時器,或者系統心跳。

  全局變量jiffies取值為自操作系統啟動以來的時鍾滴答的數目,數據類型為 unsigned long volatile (32位無符號長整型),最大取值是2^32-1。

 

2.  jiffies與秒的轉換

        將 jiffies轉換為秒,可采用公式:(jiffies/HZ)  計算。

        將 秒轉換為jiffies,可采用公式:(seconds*HZ)  計算。

   示例(本博客后面將介紹涉及到的time_before): 

    unsgned long delay = jiffies + 2*HZ;
    while(time_before(jiffies, delay)); // 忙等待兩秒,占用CPU的一個核心,期間不執行調度

 

3.  jiffies的溢出介紹

        當時鍾中斷發生時,jiffies值就加1。

        假定HZ=100,那么1個jiffies等於1/100 秒,jiffies可記錄的最大秒數為(2^32 -1)/100=42949672.95秒,約合497天或1.38年,

        當取值到達最大值時仍繼續加1,就變為了0!

        即HZ=100時,連續累加的溢出時間是一年又四個多月,如果程序對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) { /* we did not time out, call no_timeout_handler() ... */ no_timeout_handler(); } else { /* we timed out, call timeout_handler() ... */ timeout_handler(); }

  本例的意圖:

  從當前時間起,如果在0.5秒內執行完do_somework(),則調用no_timeout_handler()。如果在0.5秒后執行完do_somework(),則調用timeout_handler()。

  然后當溢出時呢? 該意圖會被打破嗎?

  假設程序開始執行前,timeout值已經接近最大值(即2^32-1 ) ,jiffies的值是(timeout-HZ/2),

       之后do_some_work執行了挺久(超過0.5秒),jiffies的值也發生了溢出(jiffies做自增操作的中途超過了32位無符號數的最大值),

  溢出后的值,可能是很小的一個數字,所以造成jiffies的值 < timeout,

  之后的代碼執行流就走到了no_time_handler()這里,這顯然和程序設計者的初衷(意圖)是違背的。

 

5.  Linux內核如何來防止jiffies溢出

  Linux內核中提供了一些宏,可有效地解決由於jiffies溢出而造成程序邏輯出錯的情況。

       PS:下圖源自Linux Kernel version 3.10.14 

     *  time_after:
     *  time_after(a,b) returns true if the time a is after time b.

  同時根據 #define time_before(a,b)    time_after(b,a) ,我們可以知道  

     * time_before(a,b) returns true if the time b is after time a.

 

6.   time_after 在驅動代碼中的應用展示

 

7.  time_after等用於時間比較的宏的原理簡介

   下面的文字摘錄自博文:https://blog.csdn.net/jk110333/article/details/8177285

    讀者先大致瀏覽一遍即可,不必糾結於絞盡腦汁的細節理解, 后面我將表達個人理解,讀者也可以直接向下瀏覽,看我的個人理解。

   /**********************************開始摘錄********************************************/

 我們仍然以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等比較時間先 /后的宏完全可以放心地用於實際的代碼中。 

   /***********************************摘錄結束******************************************/

   看完這段文字,感覺有點繞的,那么原理到底是啥呢? 是一堆數學計算嗎?是啊 ,就是這數學規律!

   凡事都是有利有弊的,針對一件事物的優化,有利處,必然帶來不利之處,從哲學角度來進行理解,事物的兩面性。

   本文第4部分,示例1介紹了jiffies的一個例子,它的弊處是會溢出,如果我們不抓住溢出這個弊處來看待這件事物,那么timeout的值可以做的很大,這是優勢。

   然而溢出是真實存在的,無法滿足客觀需求的,所以需要改進,

   從該數學規律入手進行改進后,不溢出了,這是優勢,

   但是改進后對timeout的值也縮小了使用范圍,這是為了達到該優勢所帶來的必要開銷或損耗。這就是事物的兩面性。

   

8. 示例2,對示例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)) { /* we did not time out, call no_timeout_handler() ... */ no_timeout_handler(); } else { /* we timed out, call timeout_handler() ... */ timeout_handler(); }

 

 

.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM