Linux時鍾精度:毫秒?微妙?納秒?


最近被內核時鍾精度弄的很是郁悶。具體情況如下:

掃盲:1秒=1000毫秒=1000000微妙=1000000000納秒

首先:linux有一個很重要的概念——節拍,它的單位是(次/秒)。2.6內核這個值是1000,系統中用一個HZ的宏表征這個值。同時有全局的jiffies變量,表征從開機以來經過的節拍次數(這里面還有故事,后面說,先記住這個)。當然還有wall_jiffies的牆上jiffies來表示從 07-01-1970 到現在的節拍數。每個節拍里面執行一次時鍾中斷。就是說,它的精度是毫秒

接着:內核中還有一個變量xtime表征系統的實際時間(牆上時間),定義如下。其中xtime.tv_sec以秒為單位,存放從Unix祖宗定的紀元時間(19700701)到現在的秒數。xtime.tv_nsec以納秒為單位,記錄從上一秒開始經過的納秒數。就是說,它的精度是納秒

struct timespec xtime;

struct timespec{
    time_t tv_sec;   //秒
    long tv_nsec;    //納秒
};

最后:linux提供一個gettimeofday的系統調用,它會返回一個timeval的結構體,定義如下。解釋同上,tv_sec代表牆上時間的秒,tv_usec表示從上一秒到現在經過的微秒數。就是說,它的精度是微妙

struct timeval{
    long tv_sec;     //秒
    long tv_usec;    //微妙
};

精彩的來了:
1. 內核中的xtime會在每個時鍾中斷的時候被更新一次,也就是每個節拍更新一次。你妹!!每毫秒更新一次怎么能冒出來納秒的精度??而且,內核還有可能丟失節拍。怎么能是納秒??
2. 各種書上說,gettimeofday系統調用是讀取的xtime的值。日,為啥讀出來之后精度丟了?變成微妙了?

尋尋覓覓終於理清了故事:
針對問題1:在linux啟動的時候,一個節拍的時間長度還會以納秒為單位初始化到tick_nsec中,初始化值為999848ns,坑爹啊!不到一毫秒!節拍大約為1000.15Hz。靠!實際的節拍竟然不是准確的1000!所以在每個時鍾中斷通過wall_jiffies去更新xtime的時候得到的就是一個以納秒為最小單位的的值。所以!xtime的粒度應該是不到1毫秒,也就是精度是不到1毫秒。

針對問題2:gettimeday系統調用的讀xtime代碼部分如下:
do{
    unsigned long lost;
    seq = read_seqbegin(&xtime_lock);

    usec = timer->get_offset();    //在計時器中取從上一次時鍾中斷到現在的微秒數
    lost = jiffies - wall_jiffies;
    if(lost)
        usec += lost*(1000000/HZ); //HZ是節拍宏,值1000
    sec = xtime.tv_sec;
    usec += (xtime.tv_nsec/1000);  //由納秒轉為微妙

}while(read_seqretry(&xtime_lock, seq))

while部分使用了seg鎖,只看中間的就好了。加了注釋之后就很清晰了。由於節拍可能會丟失,所以lost是丟失的節拍數(不會很多)。至於計時器就比較麻煩了,timer可能有下面四種情況。

a. 如果cur_timer指向timer_hpet對象,該方法使用HPET定時器——Inter與Microsoft開發的高精度定時器頻率至少10MHz,也就是說此時可提供真正的微妙級精度。
b. 如果cur_timer指向timer_pmtmr對象,該方法使用ACPI PMT計時器(電源管理定時器)平率大約3.58MHz,也就是說也可以提供真正的微妙級精度。
c. 如果cur_timer指向timer_tsc對象,該方法使用時間戳計數器,內置在所有8086處理器,每個CPU時鍾,計數器增加一次,頻率就是CPU頻率,所以timer精度最高。完全可以勝任微妙級的精度。
d. 如果cur_timer指向timer_pit對象,該方法使用PIT計數器,也即是最開始提到的節拍計數,頻率大概是1000Hz,此時顯然不能提供精度達到微妙的時間。所以只有這種情況是假毫秒精度!

綜上:如果使用gettimeofday系統調用,只要不要使用節拍計數器就可以保證達到微妙精度的時間(刨除進程上下文時間誤差)。至於網上說的可以拿到納秒精度的時間,看起來都是錯的。除非通過修改內核,使用時間戳計數器實現。Over!


最后最后說一個事情:jiffies的定義的是4字節,你可能猜想它初始值是0。實際上,事實並非如此!linux中jiffies被初始化為0xfffb6c20,它是一個32位有符號數,正好等於-300 000。因此,計數器會在系統啟動5分鍾內溢出。這是為了使對jiffies溢出處理有缺陷的內核代碼在開發階段被發現,避免此類問題出現在穩定版本中。



參考《深入理解linux內核》




  青春就應該這樣綻放   游戲測試:三國時期誰是你最好的兄弟!!   你不得不信的星座秘密


免責聲明!

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



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