zoukankan      html  css  js  c++  java
  • 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
    ", 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 
    ", 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 .
    ",
             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[] = {
    ");
      for (i = 0; i < 10; ++i) {
        result = sqrt(static_cast<double>(i));
        fprintf(fout, "%g,
    ", result);
      }
    
      // close the table with a zero
      fprintf(fout, "0};
    ");
      fclose(fout);
    
    #endif
    
    
    
      return 0;
    
    }
    
  • 相关阅读:
    浅谈数据的离散化
    【ZOJ】3785 What day is that day? ——浅谈KMP在ACM竞赛中的暴力打表找规律中的应用
    【Python】iichats —— 命令行下的局域网聊天程序
    【Python】iiblogs ——命令行下的网页收藏夹
    【Python】iiacm_filemaker ——简易的.cpp文件创建即初始化脚本,ACMer专用
    【黑科技】读写优化
    【POJ】1330 Nearest Common Ancestors ——最近公共祖先(LCA)
    【HDU】1717 小数化分数2 ——计数原理
    【POJ】2318 TOYS ——计算几何+二分
    【HDU】4923 Room and Moor(2014多校第六场1003)
  • 原文地址:https://www.cnblogs.com/scotth/p/9586125.html
Copyright © 2011-2022 走看看