• 计算机中的时间


    写在前面:时钟可以说是计算机的心脏,它是分时系统的基础。如果时钟反应到应用程序的层面,就是时间,很多应用程序都会涉及到时间处理。本文就来讨论一下计算机中的时钟与时间。

    1、操作系统中的时间
    在Unix/Linux系统中,有两个不同的时间:日历时间和进程时间。
    (1)日历时间:
    有些书上又叫系统时间。该值是自1970年1月1日00:00:00以来国际标准时间(U T C)所经过的秒数累计值(早期的手册称U T C为格林尼治标准时间)。在PC/AT微机系统中,支撑该时间的硬件是实时钟RT(Real Time)电路。操作系统在系统初始化的过程中,会从该电路中读取该时间,并保存在内核变量中。
    来看看Linux1.0中相关的代码

    //kernel/time.c
    extern long kernel_mktime(struct mktime * time);
    //初始化时间
    void time_init(void)
    {
        
    struct mktime time;
        
    int i;

        
    /* checking for Update-In-Progress could be done more elegantly
         * (using the "update finished"-interrupt for example), but that
         * would require excessive testing. promise I'll do that when I find
         * the time.            - Torsten
         
    */
        
    /* read RTC exactly on falling edge of update flag */
        
    for (i = 0 ; i < 1000000 ; i++)    /* may take up to 1 second */
            
    if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
                
    break;
        
    for (i = 0 ; i < 1000000 ; i++)    /* must try at least 2.228 ms*/
            
    if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
                
    break;
        
    do { /* Isn't this overkill ? UIP above should guarantee consistency */
            time.sec 
    = CMOS_READ(RTC_SECONDS);
            time.min 
    = CMOS_READ(RTC_MINUTES);
            time.hour 
    = CMOS_READ(RTC_HOURS);
            time.day 
    = CMOS_READ(RTC_DAY_OF_MONTH);
            time.mon 
    = CMOS_READ(RTC_MONTH);
            time.year 
    = CMOS_READ(RTC_YEAR);
        } 
    while (time.sec != CMOS_READ(RTC_SECONDS));
        
    if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
          {
            BCD_TO_BIN(time.sec);
            BCD_TO_BIN(time.min);
            BCD_TO_BIN(time.hour);
            BCD_TO_BIN(time.day);
            BCD_TO_BIN(time.mon);
            BCD_TO_BIN(time.year);
          }
        time.mon
    --;
        xtime.tv_sec 
    = kernel_mktime(&time);
    }


    //kernel/sched.c
    //保存系统时间的内核变量
    volatile struct timeval xtime;        /* The current time */

    //linux/mktime.h
    struct mktime {
        
    int sec;  //
        int min;  //分钟
        int hour;  //小时
        int day;   //
        int mon;  //
        int year;  //
    };
    //kernel/mktime.c
    //计算1970年1月1日00:00:00以来秒的累计值
    long kernel_mktime(struct mktime * time)
    {
        
    long res;
        
    int year;

        year 
    = time->year - 70;
    /* magic offsets (y+1) needed to get leapyears right.*/
        res 
    = YEAR*year + DAY*((year+1)/4);
        res 
    += month[time->mon];
    /* and (y+2) here. If it wasn't a leap-year, we have to adjust */
        
    if (time->mon>1 && ((year+2)%4))
            res 
    -= DAY;
        res 
    += DAY*(time->day-1);
        res 
    += HOUR*time->hour;
        res 
    += MINUTE*time->min;
        res 
    += time->sec;
        
    return res;
    }

    //linux/time.h
    struct timeval {
        
    long    tv_sec;        /* seconds */
        
    long    tv_usec;    /* microseconds */
    };

    (2)进程时间
    该时间用来度量进程使用CPU的时间。
    来看看Linux 1.0中相应的代码:

    //linux/sched.h
    //内核任务的结构定义
    struct task_struct{
    //
    //依次为:用户CPU时间,系统CPU时间,子进程用户CPU时间,子进程系统CPU时间,
    //进程开始运行时间
    long utime,stime,cutime,cstime,start_time; 
    //
    }

     当度量一个进程的执行时间时,Unix系统使用三个进程时间值:
    • 时钟时间。
    • 用户C P U时间。
    • 系统C P U时间。
    要取得任一进程的时钟时间、用户时间和系统时间很容易——只要执行命令 t i m e ( 1 ),其参数是要度量其执行时间的命令,例如:
    $ cd /usr/include
    $ time grep _POSIX_SOURCE */*.h > /dev/null
    real   0m19.81s
    user   0m0.43s
    sys    0m4.53s
    t i m e命令的输出格式与所使用的s h e l l有关。

    该时间的支撑硬件在PC机中是可编程定时芯片Intel8253(8254)。8254芯片的时钟输入频率是1193180,我们通过设定一定的初始计数值(LATCH),默认值为65535,就能控制该芯片的输出频率,默认为1193180/65535hz,例如,假定LATCH=1193180/100,我们就能保证输出频率为100hz,即周期为10ms,我们称为系统的时钟周期,或者1个系统滴答。这样,1个系统的滴答就为10ms,这也Linux的默认值。
    8254芯片每经过一个滴答时间,就会向CPU发出一个时钟中断。Linux会在时钟中断处理过程增加内核变量jiffies值,该值累计系统开机以来的经过的时钟滴答数。

    Linux 1.0中的代码:

    //kernel/sched.c
    unsigned long volatile jiffies=0//累计系统开机以来的滴答数

    2、标准C库中的时间函数

    typedef long int __clock_t;    /* Type of CPU usage counts.  */
    typedef 
    long int __time_t;


    //time.h
    typedef __clock_t clock_t;
    #define CLOCKS_PER_SEC  …
    typedef __time_t time_t;

    extern clock_t clock __P ((void));
    extern time_t time __P ((time_t *__timer));

     clock函数返回当前进程的使用处理器的时间的近似值,每秒的的时钟滴答数用宏CLOCKS_PER_SEC定义。
    在传统的C语言中,clock函数返回的类型为long(如上),但返回值实际上为unsigned long类型,long是在C语言加入unsigned long之前使用的。计算处理器时间总是使用无符号数算术。一些非标准实现中使用times函数,而不是clock函数,其返回的结构化值报告处理器时间的各个成员,通常以1/60秒为单位。如下:

    //sys/times.h
    struct tms
      {
        clock_t tms_utime;        
    /* User CPU time.  */
        clock_t tms_stime;        
    /* System CPU time.  */

        clock_t tms_cutime;        
    /* User CPU time of dead children.  */
        clock_t tms_cstime;        
    /* System CPU time of dead children.  */
      };


    /* Store the CPU time used by this process and all its
       dead children (and their dead children) in BUFFER.
       Return the elapsed real time, or (clock_t) -1 for errors.
       All times are in CLK_TCKths of a second.  
    */
    extern clock_t times __P ((struct tms *__buffer));

    标准C中函数time返回当前的日历时间,返回值类型为time_t。

    /time.h
    extern char *asctime __P ((__const struct tm *__tp));

    /* Equivalent to `asctime (localtime (timer))'.  */
    extern char *ctime __P ((__const time_t *__timer));

     这两个函数都返回时间的字符串的形式。

    //time.h

    /* Return the `struct tm' representation of *TIMER
       in Universal Coordinated Time (aka Greenwich Mean Time).  
    */
    extern struct tm *gmtime __P ((__const time_t *__timer));

    /* Return the `struct tm' representation
       of *TIMER in the local timezone.  
    */
    extern struct tm *localtime __P ((__const time_t *__timer));

    /* Used by other time functions.  */
    struct tm
    {
      
    int tm_sec;            /* Seconds.    [0-60] (1 leap second) */
      
    int tm_min;            /* Minutes.    [0-59] */
      
    int tm_hour;            /* Hours.    [0-23] */
      
    int tm_mday;            /* Day.        [1-31] */
      
    int tm_mon;            /* Month.    [0-11] */
      
    int tm_year;            /* Year    - 1900.  */
      
    int tm_wday;            /* Day of week.    [0-6] */
      
    int tm_yday;            /* Days in year.[0-365]    */
      
    int tm_isdst;            /* DST.        [-1/0/1]*/

    #ifdef    __USE_BSD
      
    long int tm_gmtoff;        /* Seconds east of UTC.  */
      __const 
    char *tm_zone;    /* Timezone abbreviation.  */
    #else
      
    long int __tm_gmtoff;        /* Seconds east of UTC.  */
      __const 
    char *__tm_zone;    /* Timezone abbreviation.  */
    #endif
    };

     gmtime与localtime将日历时间转换成stuct tm类型的分解形式,只不过前者转换成GMT时间,而后者转换成本地时间。

  • 相关阅读:
    LeetCode(35):Palindrome Number 分类: leetCode 2015-07-10 09:26 161人阅读 评论(0) 收藏
    在pycharm进行单元测试(unittest python)
    Django 基本操作
    Django中数据库操作相关的错误
    Question&&Answer
    ubuntu下 SVN 服务器搭建及使用
    python 在不同层级目录import 模块的方法
    Ubuntu 16.04安装PyCharm
    修改mysql中数据库存储主路径
    查看mysql的数据库物理存放位置
  • 原文地址:https://www.cnblogs.com/hustcat/p/1503244.html
Copyright © 2020-2023  润新知