分类: LINUX
2014-10-24 11:31:45
xtime:该变量存放当前时间和日期,是一个timespec类型的数据结构,包括tv_sec和tv_nsec两个字段,该变量每个节拍更新一次,大约每秒更新1000次,用户程序从xtime获得当前时间,内核经常会调用它。
monotonic time:该变量该机后单调递增,不因用户调整而改变,不计算系统休眠时间,系统休眠时不递增,不可以往后退。内核并没有直接定义一个特定的变量来记录monotonic时间,而是定义了一个变量wall_to_monotonic,记录了墙上时间核monotonic时间之间的偏移量,当需要获得monotonic时间时,把xtime和wall_to_monotonic相加即可。计算monotonic时间要去除系统休眠期间花费的时间,内核用total_sleep_time记录休眠的时间,每次休眠醒来后重新累加该时间,并调整wall_to_monotonic的值,使其在系统休眠醒来后,monotonic时间不会发生跳变。因为wall_to_monotonic值被调整。
raw_time:monotonic时间虽然不受settimeofday的影响,但会受到ntp调整的影响,但是raw_time不受ntp的影响,它真的就是开完机后就单调地增加。
xtime、monotonic time和raw_time可以通过用户空间的clock_gettime函数获得,对应的ID参数分别是CLOCK_REALTIME、CLOCK_MONOTONIC、CLOCK_MONOTONIC_RAW。
clock source:xtime、monotonic time、raw time都是基于该时钟源进行计时操作,当有新的精度更高的时钟源被注册时,通过timekeeping_notify函数,change_clocksource函数将会被调用,timekeeper.clock字段将会被更新,指向新的clocksource。
kernel/time/jiffies.c:内核会默认注册一个jiffies的clocksource, 如下:
1. static cycle_t jiffies_read(struct
clocksource *cs)
2. {
3. return (cycle_t) jiffies;
4. }
5.
6. struct clocksource clocksource_jiffies = {
7. .name = "jiffies",
8. .rating = 1, /* lowest valid rating*/
9. .read = jiffies_read,
10. .mask = 0xffffffff, /*32bits*/
11. .mult = NSEC_PER_JIFFY << JIFFIES_SHIFT, /* details above */
12. .mult_orig = NSEC_PER_JIFFY << JIFFIES_SHIFT,
13. .shift = JIFFIES_SHIFT,
14. };
15. static int __init init_jiffies_clocksource(void)
16. {
17. return clocksource_register(&clocksource_jiffies);
18. }
注册jiffies之后,内核会提供精度为1/HZ的时间精度,但是你的设备体系还会注册它自己的clocksource,这个clocksource是硬件相关的,会更精准,
/arch/m68k/coldfire/time.c文件:
time_init()函数,初始化xtime变量,读取RTC时间。
初始化wall_to_monotonic变量,创建时钟中断程序timer interrupt。
用户程序直接从xtime变量获得当前时间,所以内核必须周期性的更新该变量,全局时钟中断处理程序timer interrupt调用do_timer(1),每个tick调用update_times()更新xtime变量的值。其中有函数update_wall_time(),使用当前时钟源更新wall time。
在update_wall_time里会从之前注册的clocksource中读取计数值,然后将其换算为纳秒并赋值给xtime!以此为基础,xtime为用户空间提供了精准时钟。
do_gettimeofday()函数 ,首先执行usec = mach_gettimeoffset(); //用当前定时器对象的coldfire_gettimeoffset函数,确定自上一次时钟中断以来所走过的微秒数。如果定时器中断丢失,则加上相应的延迟。
usec += xtime.tv_nsec/1000; //为usec加上1秒前走过的微秒数。
sec = xtime.tv_sec; //将xtime复制到系统调用参数tv指向的缓冲区。
这里的处理要加锁xtime_lock,如果另一条内核路径获得了锁,则从开始读取。
最后的while循环用来检查微秒字段是否溢出,如果溢出则调整相应字段。
Linux指令date就是调用该函数获取时间。
Linux应用程序编程中sleep函数也要使用该函数作为时间基准。