Linux時鐘精度

chriswangyz發表於2015-08-20

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核心》

轉載:http://www.cnblogs.com/chezxiaoqiang/archive/2012/03/23/2674386.html  向作者致敬!

相關文章