Unix/Linux系统中的时间

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

1、操作系统中的时间
在Unix/Linux系统中,有两个不同的时间:日历时间和进程时间。

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

//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时间,而后者转换成本地时间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值