一般,操作体系能够运用三种办法来表明体系的其时时刻与日期:
①最简略的一种办法便是直接用一个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头文件中:
#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):
#ifndef HZ
#define HZ 100
#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):
#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):
/* LATCH is used in the interval timer and ftape setup. */
#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源文件中,如下所示:
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中,如下所示:
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
其间,成员tv_sec表明其时时刻距UNIX时刻基准的秒数值,而成员tv_usec则表明一秒之内的微秒值,且1000000>tv_usec>=0。
Linux内核经过timeval结构类型的全局变量xtime来保持其时时刻,该变量界说在kernel/timer.c文件中,如下所示:
/* The current time */
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头文件中,如下所示:
struct timezone {
int tz_minuteswest; /* minutes west of Greenwich */
int tz_dsttime; /* type of dst correction */
};
根据上述结构,Linux在kernel/time.c文件中界说了全局变量sys_tz表明体系其时所在的时区信息,如下所示:
struct timezone sys_tz;