一个 Linux 系统实际上有两个时钟:一个是电池供电的“实时时钟”(也称为 “RTC”、“CMOS 时钟”或“硬件时钟”),它在系统关闭时追踪时间,但在系统运行时不使用。另一个是“系统时钟”(有时称为“内核时钟”或“软件时钟”),它是一个基于定时器中断的软件计数器。当系统未运行时,它不存在,因此必须在启动时从 RTC(或其他时间源)初始化。“ntpd
”文档中提到的“时钟”指的是系统时钟,而不是 RTC。
这两个时钟会以不同的速率漂移,因此它们会逐渐彼此漂移,也会偏离“真实”时间。保持它们准时的最简单方法是测量它们的漂移率,并在软件中应用校正因子。由于 RTC 仅在系统未运行时使用,因此在启动时读取时钟时应用校正因子,使用 clock(8)
或 hwclock(8)
。系统时钟通过调整系统时间在每次定时器中断时前进的速率来校正,使用 adjtimex(8)
。
adjtimex(8)
的一种粗略替代方案是让 chron
定期运行 clock(8)
或 hwclock(8)
,以将系统时间同步到(已校正的)RTC。这是在 clock(8)
手册页中推荐的,并且如果您足够频繁地执行此操作以避免系统时间出现大的“跳跃”,则它会起作用,但 adjtimex(8)
是一种更优雅的解决方案。如果时间向后跳跃,某些应用程序可能会抱怨。
更精确的下一步是使用像 ntpd
这样的程序,定期从网络时间服务器或无线电时钟读取时间,并持续调整系统时钟的速率,以便时间始终匹配,而不会导致系统时间突然“跳跃”。如果您在启动时始终有网络连接,您可以完全忽略 RTC,并使用 ntpdate
(随 ntpd
软件包一起提供)从时间服务器初始化系统时钟——可以是 LAN 上的本地服务器,也可以是互联网上的远程服务器。但是,如果您有时没有网络连接,或者如果您需要在网络激活之前的启动序列期间时间准确,那么您还需要维护 RTC 中的时间。
如果您正在使用像 ntpd
这样的程序,您可能认为应该将 RTC 同步到(已校正的)系统时钟,这似乎是显而易见的。但是,如果系统要关闭的时间超过几分钟,这将被证明是一个坏主意,因为它会干扰在启动时将校正因子应用于 RTC 的程序。
如果系统 24/7 全天候运行,并且每次关闭后都立即重启,那么您可以在重启前立即从系统时钟设置 RTC。RTC 的漂移不足以在重启所需的时间内产生差异,因此您不需要知道其漂移率。
当然,系统可能会意外宕机,因此,如果系统时钟已被另一个程序调整过,则某些版本的内核每 11 分钟将 RTC 同步到系统时钟。RTC 在 11 分钟内不会漂移到足以产生任何差异,但如果系统宕机的时间足够长,以至于 RTC 发生显著漂移,那么您就会遇到问题:将漂移校正应用于 RTC 的程序需要 *确切地* 知道它上次何时重置,而内核不会在任何地方记录该信息。
一些 Unix “传统主义者”可能会想知道为什么有人会运行 Linux 系统不到 24/7,但我们中的一些人运行双启动系统,在某些时候运行另一个操作系统,或者在笔记本电脑上运行 Linux,当不使用时必须关闭以节省电池电量。其他人只是不喜欢让机器长时间无人值守地运行(即使我们已经听过所有支持它的论点)。因此,“每 11 分钟”的功能变成了一个错误。
这个“功能/错误”在不同版本的内核(以及可能在不同版本的 xntpd
和 ntpd
中)中表现不同,因此,如果您同时运行 ntpd
和 hwclock
,您可能需要测试您的系统以查看它实际执行的操作。如果您无法阻止内核重置 RTC,您可能必须在没有 RTC 校正因子的情况下运行。
控制内核这一部分的代码可以在 /usr/src/linux-2.0.34/arch/i386/kernel/time.c
中找到(路径中的版本号将是您正在运行的内核版本)。如果变量 time_status
设置为 TIME_OK
,则内核每 11 分钟会将系统时间写入 RTC,否则它会保持 RTC 不变。对 adjtimex(2)
的调用(例如,ntpd
和 timed
使用的)可能会启用此功能。对 settimeofday(2)
的调用会将 time_status
设置为 TIME_UNSYNC
,这会告诉内核不要调整 RTC。我没有找到关于此的任何真正的文档。
我听说有报告称,某些版本的内核可能在“睡眠模式”中存在问题,这些模式会关闭 CPU 以节省能源。最好的解决方案是保持内核更新到最新版本,并将任何问题提交给维护内核的人员。
如果您从 RTC 获得奇怪的结果,您可能遇到了硬件问题。一些 RTC 芯片包含一个锂电池,可能会耗尽,而一些主板有一个外部电池的选项(请确保跳线设置正确)。同一个电池维护 CMOS RAM,但时钟需要更多电量,并且可能首先失效。系统时钟出现奇怪的结果可能意味着中断存在问题。
Linux “系统时钟”实际上只是计算自 1970 年 1 月 1 日以来经过的秒数,并且始终采用 UTC(或 GMT,在技术上有所不同,但对于普通用户来说,两者往往可以互换使用)。UTC 不会随着 DST 的到来和离去而改变——改变的是 UTC 和本地时间之间的转换。转换为本地时间由链接到应用程序的库函数完成。
这有两个后果:首先,任何需要知道本地时间的应用程序也需要知道您所在的时区,以及 DST 是否生效(有关时区的更多信息,请参见下一节)。其次,内核中没有提供在 DST 到来和离去时更改系统时钟或 RTC 的功能,因为 UTC 不会改变。因此,仅运行 Linux 的机器应将 RTC 设置为 UTC,而不是本地时间。
但是,许多人运行双启动系统,其中其他操作系统期望 RTC 包含本地时间,因此 hwclock
需要知道您的 RTC 是本地时间还是 UTC,然后将其转换为自 1970 年 1 月 1 日以来的秒数(UTC)。这仍然没有为 RTC 的季节性变化提供支持,因此更改必须由其他操作系统进行(这是关于不允许超过一个程序更改 RTC 中的时间的规则的唯一例外)。
不幸的是,RTC 或 CMOS RAM 中没有标志来指示标准时间与 DST,因此每个操作系统都将此信息存储在其他操作系统找不到的位置。这意味着 hwclock
必须假设 RTC 始终包含正确的本地时间,即使自最近一次季节性时间更改以来尚未运行其他操作系统。
如果在发生季节性时间更改时 Linux 正在运行,则系统时钟不受影响,应用程序将进行正确的转换。但是,如果 linux 由于任何原因必须重启,则系统时钟将被设置为 RTC 中的时间,这将偏离一个小时,直到其他操作系统(通常是 Windows)有机会运行。
没有办法解决这个问题,但 Linux 不会经常崩溃,因此在双启动系统上重启的最可能原因是运行其他操作系统。但是,如果您是那些只要一段时间不使用 Linux 就会关闭 Linux 的人之一——如果您自上次时间更改以来没有机会运行其他操作系统,则 RTC 将会偏离一个小时,直到您运行其他操作系统为止。
一些其他文档声明将 RTC 设置为 UTC 可以让 Linux 正确处理 DST。这并非完全错误,但它没有讲述全部故事——只要您不重启,RTC 中是什么时间(甚至 RTC 的电池是否没电)都无关紧要。Linux 无论如何都会保持正确的时间,直到下次重启。理论上,如果您每年只重启一次(对于 Linux 来说并非不合理),DST 可能会来来去去,您永远不会注意到 RTC 已经错误了几个月,因为系统时钟一直保持正确。但是,由于您无法预测何时想要重启,因此如果您没有运行另一个需要本地时间的操作系统,最好将 RTC 设置为 UTC。
Dallas Semiconductor RTC 芯片(它是 IBM AT 和克隆机中使用的 Motorola 芯片的直接替代品)实际上具有自行进行 DST 转换的能力,但此功能未使用,因为转换日期硬连线到芯片中并且无法更改。当前版本在四月的第一个星期日和十月的最后一个星期日更改,但早期版本使用不同的日期(显然这在其他使用其他日期的国家/地区不起作用)。此外,RTC 通常集成到主板的“芯片组”中(而不是作为单独的芯片),我不知道它们是否都具有此能力。
您可能在安装 Linux 时正确设置了时区。但是,如果您由于某种原因必须更改它,或者如果有关 DST 的当地法律已更改(就像在某些国家/地区经常发生的那样),那么您需要知道如何更改它。如果您的系统时间偏差了几个小时,您可能遇到了时区问题(或 DST 问题)。
时区和 DST 信息存储在 /usr/share/zoneinfo
中(或旧系统上的 /usr/lib/zoneinfo
中)。本地时区由从 /etc/localtime
到这些文件之一的符号链接确定。更改时区的方法是更改链接。如果您的本地 DST 日期已更改,您将必须编辑该文件。
您还可以使用 TZ
环境变量来更改当前时区,如果您远程登录到另一个时区的机器,这非常方便。另请参见 tzset
和 tzfile
的手册页。
这在 http://www.linuxsa.org.au/tips/time.html 中得到了很好的总结
如果您不需要亚秒级精度,hwclock(8)
和 adjtimex(8)
可能就是您所需要的全部。很容易对时间服务器和无线电时钟等感到兴奋,但我多年来一直运行旧的 clock(8)
程序,效果非常好。另一方面,如果您在 LAN 上有多台机器,让它们自动将时钟彼此同步可能很方便(有时是必要的)。即使您真的不需要其他东西,玩玩其他东西也可能很有趣。
在仅运行 Linux 的机器上,将 RTC 设置为 UTC(或 GMT)。在需要 RTC 中本地时间的双启动系统上,请注意,如果您在季节性时间更改后必须重启 Linux,则时钟可能会暂时偏差一个小时,直到您有机会运行其他操作系统。如果您运行两个以上的操作系统,请确保只有一个操作系统尝试调整 DST。