linux系统里的时间

系统 Linux
一提到时间,大家就会提到身边事物、概念等,如:手表、时钟等等。在本文就给大家介绍下linux系统里的时间是如何构成和运行的。

通常,linux操作系统可以使用三种方法来表示系统的当前时间与日期:
①最简单的一种方法就是直接用一个64位的计数器来对时钟滴答进行计数。
②第二种方法就是用一个32位计数器来对秒进行计数,同时还用一个32位的辅助计数器对时钟滴答计数,之子累积到一秒为止。因为232超过136年,因此这种方法直至22世纪都可以让系统工作得很好。
③第三种方法也是按时钟滴答进行计数,但是是相对于系统启动以来的滴答次数,而不是相对于相对于某个确定的外部时刻;当读外部后备时钟(如RTC)或用户输入实际时间时,根据当前的滴答次数计算系统当前时间。
UNIX类操作系统通常都采用第三种方法来维护系统的时间与日期。

1 基本概念
首先,有必要明确一些Linux内核时钟驱动中的基本概念。
(1)时钟周期(clock cycle)的频率:8253/8254 PIT的本质就是对由晶体振荡器产生的时钟周期进行计数,晶体振荡器在1秒时间内产生的时钟脉冲个数就是时钟周期的频率。
Linux用宏CLOCK_TICK_RATE来表示8254 PIT的输入时钟脉冲的频率(在PC机中这个值通常是1193180HZ),该宏定义在include/asm-i386/timex.h头文件中: 

  1. #define CLOCK_TICK_RATE 1193180 /* Underlying HZ */ 


(2)时钟滴答(clock tick):我们知道,当PIT通道0的计数器减到0值时,它就在IRQ0上产生一次时钟中断,也即一次时钟滴答。PIT通道0的计数器的初始值决定了要过多少时钟周期才产生一次时钟中断,因此也就决定了一次时钟滴答的时间间隔长度。
(3)时钟滴答的频率(HZ):也即1秒时间内PIT所产生的时钟滴答次数。类似地,这个值也是由PIT通道0的计数器初值决定的(反过来说, 确定了时钟滴答的频率值后也就可以确定8254 PIT通道0的计数器初值)。Linux内核用宏HZ来表示时钟滴答的频率,而且在不同的平台上HZ有不同的定义值。对于ALPHA和IA62平台HZ的 值是1024,对于SPARC、MIPS、ARM和i386等平台HZ的值都是100。该宏在i386平台上的定义如下(include/asm- i386/param.h):

  1. #ifndef HZ   
  2. #define HZ 100   
  3. #endif   

根据HZ的值,我们也可以知道一次时钟滴答的具体时间间隔应该是(1000ms/HZ)=10ms。
(4)时钟滴答的时间间隔:Linux用全局变量tick来表示时钟滴答的时间间隔长度,该变量定义在kernel/timer.c文件中,如下:
long tick = (1000000 + HZ/2) / HZ; /* timer interrupt period */
tick变量的单位是微妙(μs),由于在不同平台上宏HZ的值会有所不同,因此方程式tick=1000000÷HZ的结果可能会是个小数, 因此将其进行四舍五入成一个整数,所以Linux将tick定义成(1000000+HZ/2)/HZ,其中被除数表达式中的HZ/2的作用就是用来将 tick值向上圆整成一个整型数。
另外,Linux还用宏TICK_SIZE来作为tick变量的引用别名(alias),其定义如下(arch/i386/kernel/time.c):

  1. #define TICK_SIZE tick   

(5)宏LATCH:Linux用宏LATCH来定义要写到PIT通道0的计数器中的值,它表示PIT将没隔多少个时钟周期产生一次时钟中断。显然LATCH应该由下列公式计算:
LATCH=(1秒之内的时钟周期个数)÷(1秒之内的时钟中断次数)=(CLOCK_TICK_RATE)÷(HZ)
类似地,上述公式的结果可能会是个小数,应该对其进行四舍五入。所以,Linux将LATCH定义为(include/linux/timex.h):

  1. /* LATCH is used in the interval timer and ftape setup. */   
  2. #define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */   

类似地,被除数表达式中的HZ/2也是用来将LATCH向上圆整成一个整数。

2 表示系统当前时间的内核数据结构
作为一种UNIX类操作系统,Linux内核显然采用本节一开始所述的第三种方法来表示系统的当前时间。Linux内核在表示系统当前时间时用到了三个重要的数据结构:
①全局变量jiffies:这是一个32位的无符号整数,用来表示自内核上一次启动以来的时钟滴答次数。每发生一次时钟滴答,内核的时钟中断处 理函数timer_interrupt()都要将该全局变量jiffies加1。该变量定义在kernel/timer.c源文件中,如下所示:

  1. unsigned long volatile jiffies;   

C语言限定符volatile表示jiffies是一个易该变的变量,因此编译器将使对该变量的访问从不通过CPU内部cache来进行。
②全局变量xtime:它是一个timeval结构类型的变量,用来表示当前时间距UNIX时间基准1970-01-01 00:00:00的相对秒数值。结构timeval是Linux内核表示时间的一种格式(Linux内核对时间的表示有多种格式,每种格式都有不同的时间 精度),其时间精度是微秒。该结构是内核表示时间时最常用的一种格式,它定义在头文件include/linux/time.h中,如下所示:

  1. struct timeval {   
  2. time_t tv_sec; /* seconds */   
  3. suseconds_t tv_usec; /* microseconds */   
  4. };   

其中,成员tv_sec表示当前时间距UNIX时间基准的秒数值,而成员tv_usec则表示一秒之内的微秒值,且1000000>tv_usec>=0。
Linux内核通过timeval结构类型的全局变量xtime来维持当前时间,该变量定义在kernel/timer.c文件中,如下所示:

  1. /* The current time */   
  2. volatile struct timeval xtime __attribute__ ((aligned (16)));   

    但是,全局变量xtime所维持的当前时间通常是供用户来检索和设置的,而其他内核模块通常很少使用它(其他内核模块用得最多的是 jiffies),因此对xtime的更新并不是一项紧迫的任务,所以这一工作通常被延迟到时钟中断的底半部分(bottom half)中来进行。由于bottom half的执行时间带有不确定性,因此为了记住内核上一次更新xtime是什么时候,Linux内核定义了一个类似于jiffies的全局变量 wall_jiffies,来保存内核上一次更新xtime时的jiffies值。时钟中断的底半部分每一次更新xtime的时侯都会将 wall_jiffies更新为当时的jiffies值。全局变量wall_jiffies定义在kernel/timer.c文件中:
/* jiffies at the most recent update of wall time */
unsigned long wall_jiffies;
③全局变量sys_tz:它是一个timezone结构类型的全局变量,表示系统当前的时区信息。结构类型timezone定义在include/linux/time.h头文件中,如下所示:

  1. struct timezone {   
  2. int tz_minuteswest; /* minutes west of Greenwich */   
  3. int tz_dsttime; /* type of dst correction */   
  4. };   

基于上述结构,Linux在kernel/time.c文件中定义了全局变量sys_tz表示系统当前所处的时区信息,如下所示:

  1. struct timezone sys_tz;  

通过对上面文章的了解,大家对Linux系统下的时间管理有了深一步的认识,对Linux系统下时间的构成和运行有了自己的心得。

【编辑推荐】

  1. 1996-2010:那些重要的Linux系统病毒们
  2. 在主流linux系统上运行MeeGo SDK
  3. 在Linux系统中安装系统日志服务器
  4. 安装linux系统可以用的5种方法
  5. 嵌入式linux系统开发总结
  6. Linux系统移植
责任编辑:赵鹏
相关推荐

2014-02-17 15:27:19

Linux系统

2009-11-11 10:22:15

linux系统时间操作系统

2011-06-29 09:32:05

Linux

2019-06-28 11:26:31

LinuxWindowsTcping

2010-02-25 14:30:14

Linux php

2009-12-14 15:35:41

Linux操作系统

2011-02-17 10:14:44

LinuxCPU负载

2017-04-12 14:30:45

Linux内核DebugFS

2017-12-11 09:04:53

LinuxScrot截屏

2009-12-11 14:25:42

Linux查看系统时间

2009-06-10 08:49:05

LinuxExt3恢复

2016-12-01 18:39:58

Linux识别USB设备

2021-08-09 11:31:54

Linux重复相片删除

2020-08-23 21:45:18

Linux终端文件

2015-07-27 11:34:03

Linux内核指针

2018-04-08 14:27:45

Linuxuptime系统运行时间

2022-03-28 19:19:45

Linux时间子系统

2009-06-10 08:41:15

Linux脚本语言

2009-12-25 17:05:50

Linux操作系统

2024-02-21 21:28:29

Linux系统
点赞
收藏

51CTO技术栈公众号