delphij's Chaos

选择chaos这个词是因为~~实在很难找到一个更合适的词来形容这儿了……

28 Aug 2010

RTC 与 NTP

早期版本的 FreeBSD 有一个问题是,如果使用 ntpd 来校准系统时钟的话,如果长期开机,则在重启之后,时间会差很远,之前一直没仔细研究这个问题。最近突然发现 FreeBSD 8.1-RELEASE 里面没有这个问题了,于是翻看了一下代码和之前的一些邮件才明白是怎么回事,总结一下:

一般来说操作系统在引导以后会自行维护时钟,简单地说,就是操作系统根据某个时钟源,例如单CPU系统中的TSC,或多CPU系统中比较常用的高精度事件计时器HPET等等,计算出时间的流逝并修改系统时间。只有在系统刚刚引导的时候,才会直接取 RTC 时间(在关机时,主板上的电池仍可维持 RTC 电路的计时功能)。

我们知道,任何测量工具都是有误差的,普通的 PC 系统上配置的计时装置肯定不会做的非常精密,这也是我们为什么要运行 ntpd 的主要原因。在系统运行的过程中,这些误差不断地积累,使用 ntpd 时,系统可以计算出所采用的时钟源与实际的时间之间的误差,并逐步调整时间源的计数周期与实际时间的比例,从而维持系统时钟的精确性。

在重启时,在内存中维护的系统时刻数据便随之丢弃了,操作系统再次启动时读取的是先前未经修正过的、继续在积累误差的 RTC 时间数据(注意:RTC是不依赖于OS运行的;OS可以修改其时刻数据,但无论开机还是关机,只要维持其需要的电流即可确保 RTC 继续自行更新,而无需 OS 干预)。

FreeBSD先前的实现中,只有非常少的地方调用了 resettodr(9) 来修正 RTC 时间,这些地方主要是settimeofday()之类的直接设置内核时刻数据的点,而对于 ntp 的情况,则没有相应的处理。

新的实现 (以r207360为主体,包含r207359, r207362)则增加了一个callout,每半小时将系统时钟刷回RTC一次,从而解决了这个问题。

关于为什么不应该使用 ntpdate 来对时(ntpdate会使用settimeofday()),请参考 A core这篇 介绍 NTP 的文章,以及我的 这篇 文章。