linux 時間處理 + 簡單寫log



1s ==1000ms == 1,000,000us == 1,000,000,000 nanosecond


uname -a
Linux scott-Z170X 4.15.0-34-generic #37-Ubuntu SMP Mon Aug 27 15:21:48 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
intel i5-6500

我的CLOCKS_PER_SEC   是100W
---------------------------

//格式化輸出
int64_t   %ld ,    uint64_t  %lu
longlong  %lld ,  ulonglong  %llu


double  %g
float   %f
int %d
uint %u

//指定輸出到哪個地方,可以指定log文件 ,或者控制台 stdout
fprintf(stdout, "%g is %g\n", inputValue, outputValue);

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


首先: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內核》


#include <stdio.h>   //file 

#include <iostream> 
#include <ctime>         //c++   精確度s   time(),ctime()
#include <string>

#include <sys/time.h>  //linux  精確度s, us    gettimeofday()


using namespace std;

int main()
 {
        time_t beginTime,endTime;
        struct timeval tvBegin,tvEnd;
        struct timezone tz;

 
        time (&beginTime);
        printf ("The current local time is: %s", ctime (&beginTime));
        gettimeofday (&tvBegin , &tz);

        //do  something
    
        time (&endTime);
        printf ("The current local time is: %s \n", ctime (&endTime));
        gettimeofday (&tvEnd , &tz);

        //tv_sec 是 Unix timestamp
        //tvEnd.tv_sec-tvBegin.tv_sec 得到多少s,   ==>1 s*1000  (5-3=2, 2*1000=>2000ms) 
        //tvEnd.tv_usec-tvBegin.tv_usec 得到多少us,==>1 us/1000 (55100-373988= -31812, 2/1000=>318ms)
        //2000ms+(-318ms) 就是最終消耗的時間 1682ms


        uint64_t uSpendms = (tvEnd.tv_sec-tvBegin.tv_sec)*1000+(tvEnd.tv_usec-tvBegin.tv_usec)/1000;


#ifdef TEST 
        printf ("uSpendms = %lu .tvEnd.tv_sec = %ld, tvBegin.tv_sec=%ld ;  tvEnd.tv_usec = %ld, tvBegin.tv_usec=%ld .\n",
         uSpendms,
         tvEnd.tv_sec, tvBegin.tv_sec, 
         tvEnd.tv_usec, tvBegin.tv_usec); 
#else
        std::cout<< "tvEnd.tv_sec ="<<tvEnd.tv_sec << ". tvBegin.tv_sec ="<<tvBegin.tv_sec <<std::endl;
        std::cout<< "tvEnd.tv_usec ="<<tvEnd.tv_usec << ". tvBegin.tv_usec ="<<tvBegin.tv_usec <<std::endl;
        std::cout<< "SpendTime="<<uSpendms<<"ms"<<std::endl;

#endif     



#ifdef  WRITE_LOG

  int i;
  double result;

  // open the output file
  FILE* fout = fopen(“test.log”, "w");
  if (!fout) {
    return 1;
  }

  // create a source file with a table of square roots
  fprintf(fout, "double sqrtTable[] = {\n");
  for (i = 0; i < 10; ++i) {
    result = sqrt(static_cast<double>(i));
    fprintf(fout, "%g,\n", result);
  }

  // close the table with a zero
  fprintf(fout, "0};\n");
  fclose(fout);

#endif



  return 0;

}


免責聲明!

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



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