您现在的位置: 天下网吧 >> 网吧天下 >> 网吧技术 >> Linux专版 >> 正文

Linux时间管理之 hardware

[作者:佚名 来源:不详 时间:2014-10-9我来说两句

  一直以来对Linux下的时间管理知之不详,GFree_wind在微博发起过几次Linux下时钟的讨论,和Godbach这些大牛比,我完全插不上话,因为不懂。近来闲暇时间研究了下Linux下的时间管理,分享出来,请大家指正。

  从我们大白话的角度想,时间管理其实分成两部分,就像我们小时候学习物理的时候物理老师不断强调时间和时刻的区别。一个是时刻,比如现在是20:44:37秒,指的是时刻,我们手机上看时间,指的也是时刻。另一块是时间,比如说,我每天工作八小时,再比如说,半小时之后,我要出门了,结束时间指向的是未来,但是仍然是一段时间。OK。无论是时刻还是时望间,都是需要硬件支持的,你手里只有一块最小刻度只有1秒的手表,就不要指用这块手表给百米大赛度量成绩了,何哉,硬件太挫。Linux也是如此,之所以Linux启动之后,可以精确的计时,那是因为Linux的下面有相应的硬件为依托。

  RTC

  RTC,real time clock,实时时钟和其他的硬件是不同的,RTC吐出来的是时刻,而其他硬件时钟吐出来的是时间。也就是说,RTC能告诉我们,当前是2013年9月12日,21:49:38,但是其他的硬件如TSC,PIT,HPET只能告诉我们,我应该走过了XX个cycle,按照我的频率,已经过去了10分钟了。

  为啥RTC这么牛X,可以告诉我们当前时刻,哪怕用户关了机?以X86为例,RTC是主板上的一块CMOS芯片,哪怕你的Linux关了机,她也可以依赖主板上的电池维持时钟的准确。当然了,在Linux下,RTC存储的是UTC时间,而不会考虑timezone。

  所以,Linux启动的时候,一定会拜访RTC来获得当前的时刻值,尽管精度不高(精确到秒)。When and How?

  首先回答When。Linux启动的时候,start_kernel有四大time相关的函数调用:

  init_timers();

  hrtimers_init();

  timekeeping_init()

  time_init();

  从RTC中读取当前的UTC时间是timekeeping_init中做的事情,调用路径如下:

  timekeeping_init

  |___________read_persistent_clock (arch/x86/kernel/rtc.c)

  |_____x86_platform.get_wallclock()

  |_____mach_get_cmos_time (arch/x86/kernel/x86_init.c)

  /************arch/x86/kernel/rtc.c*****************/ void read_persistent_clock(struct timespec *ts) { unsigned long retval; retval = x86_platform.get_wallclock(); ts->tv_sec = retval; ts->tv_nsec = 0; } /*****************arch/x86/kernel/x86_init.c ****************/ struct x86_platform_ops x86_platform = { .calibrate_tsc = native_calibrate_tsc, .wallclock_init = wallclock_init_noop, .get_wallclock = mach_get_cmos_time, .set_wallclock = mach_set_rtc_mmss, .iommu_shutdown = iommu_shutdown_noop, .is_untracked_pat_range = is_ISA_range, .nmi_init = default_nmi_init, .get_nmi_reason = default_get_nmi_reason, .i8042_detect = default_i8042_detect, .save_sched_clock_state = tsc_save_sched_clock_state, .restore_sched_clock_state = tsc_restore_SChed_clock_state, }

  对于我们而言,我们要读的function是mach_get_cmos_time

  unsigned long mach_get_cmos_time(void) { unsigned int status, year, mon, day, hour, min, sec, century = 0; unsigned long flags; spin_lock_irqsave(&rtc_lock, flags); /* * If UIP is clear, then we have >= 244 micROSeconds before * RTC registers will be updated. Spec sheet says that this * is the reliable way to read RTC - registers. If UIP is set * then the register access might be invalid. */ while ((CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)) cpu_relax(); sec = CMOS_READ(RTC_SECONDS); min = CMOS_READ(RTC_MINUTES); hour = CMOS_READ(RTC_HOURS); day = CMOS_READ(RTC_DAY_OF_MONTH); mon = CMOS_READ(RTC_MONTH); year = CMOS_READ(RTC_YEAR); #ifdef CONFIG_ACPI if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID && acpi_gbl_FADT.century) century = CMOS_READ(acpi_gbl_FADT.century); #endif status = CMOS_READ(RTC_CONTROL); WARN_ON_ONCE(RTC_ALWAYS_BCD && (status & RTC_DM_BINARY)); spin_unlock_irqrestore(&rtc_lock, flags); if (RTC_ALWAYS_BCD || !(status & RTC_DM_BINARY)) { sec = bcd2bin(sec); min = bcd2bin(min); hour = bcd2bin(hour); day = bcd2bin(day); mon = bcd2bin(mon); year = bcd2bin(year); } if (century) { century = bcd2bin(century); year += century * 100; printk(KERN_INFO "Extended CMOS year: %d\n", century * 100); } else year += CMOS_YEARS_OFFS; return mktime(year, mon, day, hour, min, sec); }

  这一段代码已经牵扯到了硬件相关的编程,我们其实并不关心驱动,对于硬件比较感兴趣的筒子,可以访问http://xenyinzen.wikidot.com/reship:080225-2

  获得更多更详细的信息。mktime是将年月日时分秒组装成1970年1月1日00:00:00这个UNIX基准时间以来的秒数。我们在Linux下可以通过一下方式获得这个值:

  root@manu:/sys/class/rtc/rtc0# date +%s ;cat /sys/class/rtc/rtc0/since_epoch 1379081060 1379081060

  既然Linux上电的时候,可以从RTC中读出当前的时间,我们也可以设置时间,并且写入到RTC。用户层也可以操作RTC硬件时钟,通过ioctl。下面给出一个样例:

  #include #include #include #include #include int main(int argc,char *argv[]) { int retval,fd; struct rtc_time rtc_tm; fd=open("/dev/rtc",O_RDONLY); if(fd==-1) { perror("error open /dev/rtc"); return -1; } retval=ioctl(fd,RTC_RD_TIME,&rtc_tm); if(retval==-1) { perror("error exec RTC_RD_TIME ioctl"); return -2; } printf("RTC time is %d-%d-%d %d:%d:%d \n", rtc_tm.tm_year+1900,rtc_tm.tm_mon,rtc_tm.tm_mday, rtc_tm.tm_hour,rtc_tm.tm_min,rtc_tm.tm_sec); close(fd); return 0; }

  输出如下:

  root@manu:~/code/c/self/rtc# ./rtc_test RTC time is 2013-8-14 15:46:2

  对于set RTC 也可以通过ioctl RTC_SET_TIME参数来实现,我就不多说了,对这部分感兴趣的,可以自行man rtc,或者Kernel的Documentation/rtc.txt有一个示例代码,比较详细。

  已经说过了,RTC在时间相关的硬件中是个独树一帜的奇葩,作用和其他的硬件不同。而其他的硬件只是以一定的频率产生时钟中断,帮助OS完成计时。前面我也提到过,你手里拿着个手表,就不要指望给百米大赛计时,原因就是精度太低。硬件也是如此,有精度高的有精度低的。Linux操作系统抽象出了clocksource(时钟源)来管理这些硬件。Linux会在所有的硬件时钟中选择出精度最高作为当前在用的时钟源。

  如何查看当前所有的可用的时钟源已经当前在用的时钟源呢?

  manu@manu:/$ cat /sys/devices/system/clocksource/clocksource0/available_clocksource tsc hpet acpi_pm manu@manu:/$ cat /sys/devices/system/clocksource/clocksource0/current_clocksource tSC

关注天下网吧微信,了解网吧网咖经营管理,安装维护:


本文来源:不详 作者:佚名

我来说两句(请遵守法律法规)
声明
本文来源地址:http://soft.chinabyte.com/os/
声明:本站所发表的文章、评论及图片仅代表作者本人观点,与本站立场无关。若文章侵犯了您的相关权益,请及时与我们联系,我们会及时处理,感谢您对本站的支持!联系邮箱:support@txwb.com.,本站所有有注明来源为天下网吧或天下网吧论坛的原创作品,各位转载时请注明来源链接!
天下网吧 网吧天下

扫一扫,关注天下网吧微信