SDB:配置时钟
情况
使用诸如date命令显示的时钟在重启后不正确。有时某些文件的显示时间戳不正确,要么回溯到过去,要么前进到未来。启动时,文件系统可能会被检查,而没有明显的原因。
背景
本地时间
在安装 Linux 系统期间,大多数用户会被询问要为 CMOS/BIOS 时钟使用的时间。此时,大多数用户会故意选择与手腕上手表显示的本地时间相同的时间。建议将系统时钟(以及 CMOS/BIOS 时钟)设置为 UTC/GMT。这样,Linux 可以在夏令时变化时保持用户空间时钟的正确时间。最安全的方法是在开始安装过程之前将 CMOS 时钟设置为 UTC。
协调世界时
UTC(协调世界时)是国际时间标准。它是通常被称为格林威治标准时间 (GMT) 的术语的当前术语。零小时 UTC 是英国格林威治的午夜,格林威治位于零经度子午线上。协调世界时基于 24 小时制时钟;因此,下午 4 点 UTC 表示为 16:00 UTC。
用户空间
为了理解 CMOS 时钟中的本地时间可能导致问题的原因,了解 Linux 内核使用哪个时钟作为参考以及用户空间工具如何访问该时钟会有所帮助。每个应用程序(如用户空间工具)首先会使用例如系统调用time(2)来确定自 Epoch 以来的秒数(1970 年 1 月 1 日 00:00:00 UTC)。现在,这个数字将使用 glibc API 函数ctime(3), gmtime(3)和localtime(3)来返回本地时间。
为了转换自 Epoch 以来的秒数,glibc API 函数需要时区信息,即用于本地时间的 UTC 时间偏移量以及必须应用的夏令时 (DST) 等规则。
所有这些时区信息都存储在以下位置/usr/share/zoneinfo/例如:/usr/share/zoneinfo/US/Central依赖关系解析(安装两个新软件包)由 YaST 使用/usr/share/zoneinfo/Europe/Berlin要应用这样的规则,可以使用环境变量TZ例如:
或者对于在 shell 会话中运行的所有应用程序:
export TZ
对于 (t)csh 用户:
这将为当前 shell 会话中此定义之后的 所有 命令声明时区。
为了为当前在用户空间中运行的所有应用程序选择时区,glibc API 函数还会搜索文件
/etc/localtime
这可以是对以下 zonedate 文件的一个符号链接/usr/share或者,如果/usr有自己的分区,则是对以下 zonedate 文件的副本/usr/share/zoneinfo/请注意,当您这样做时,无论更改是通过命令行还是 YAST 完成的,您都需要更新初始 ramdisk。
内核空间
现在问题来了:内核从哪里知道自 Epoch 以来的秒数,如1970 年 1 月 1 日 00:00:00 UTC? 在启动时,内核从固件时钟(如 BIOS/CMOS)读取此信息。有了这个,可以看到用户空间中正确本地时间的以下方案
[CMOS clock] -> [System Clock of the kernel in UTC]
|
v
[glibc] <- /etc/localtime or TZ environment variable
|
v
[localtime in user space]
为了使之工作,并且由于内核没有任何关于本地时间和其时区规则(如时区偏移量和其他规则)的信息,因此 CMOS/BIOS 时钟自然应该设置为 UTC。
硬件时钟
在某些系统上,有多个操作系统 (OS),并且其中一些其他 OS 可能无法将 UTC 作为自然时钟参考处理。这可能需要为 CMOS/BIOS 时钟选择本地时间,但这有两个缺点
- 必须在访问任何文件系统(可能通过文件系统检查)和挂载该文件系统之前,将此事实告知 Linux 内核,以应用正确的文件系统时间戳。与邮件 The ext3 way of journalling 在 https://lkml.org 上找到的线程进行比较。
- CMOS/BIOS 时钟不知道夏令时切换。这可能随着新的 UEFI BIOS 方法而改变。
Initrd RAM 磁盘
为了解决第一个缺点,Linux 内核的系统时钟可以通过系统调用settimeofday(2)告知应该使用哪个时区偏移量。这需要settimeofday(2)也作为 glibc 的一部分,/etc/localtime以及/usr/share/zoneinfo/UTC在访问根文件系统之前可用。
为此,文件/etc/localtime和/usr/share/zoneinfo/UTC与工具一起存储warpclock在以下位置:/lib/mkinitrd/在initrd(4)初始引导加载程序 RAM 磁盘。上述方案现在变得更加复杂
[CMOS clock] -> [System Clock of the kernel in localtime]
^
|
+- [initrd warpclock <- (initrd)/etc/localtime]
|
v
[System Clock of the kernel in UTC]
|
v
[glibc] <- /etc/localtime or TZ environment variable
|
v
[localtime in user space]
剩下的缺点是 CMOS/BIOS 时钟必须在夏令时切换时更改。这可以由系统管理员完成,也可以通过引导 Windows[tm] 系统完成,该系统将其深深地存储在 Windows[tm] 注册表中(缺点是其他 OS 无法知道其 CMOS/BIOS 时钟校正)。
结论
现在关于应该为 BIOS/CMOS 时钟使用哪个时间参考
- 在仅使用 Linux 引导的单个系统上,UTC 似乎是唯一的自然参考。这是因为 CMOS/BIOS 时钟不再需要由于夏令时切换而进行维护。
- 在多引导系统中,任何无法在 CMOS/BIOS 时钟中处理 UTC 的操作系统,唯一的选择是本地时间。
后者需要更多的准确性来保持时区和 CMOS 时钟在 initrd 中的同步,以及当前的设置。也就是说,时区的任何更改不仅需要更改 CMOS 时钟,还需要在下次重新启动之前刷新 initrd 才能访问任何文件系统。
其他操作系统
对于 Vista[tm] SP2、Windows[tm] 7 和 Server 2008 R2,这些 Windows[tm] 系统的系统管理员可以创建一个带有扩展名的文件.reg例如,命名为utc.reg:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation] "RealTimeIsUniversal"=dword:00000001
然后双击它将其内容与 Windows[tm] 注册表合并。这样,就可以为这两个 Windows[tm] 系统和 Linux 系统使用 UTC 作为 CMOS 时钟的参考。据报道,Vista[tm] SP2 无法处理夏令时切换。
与外部链接 IBM PC Real Time Clock should run in UT 进行比较。
接下来,问题可能是 Windows[tm] 中的 增强写入过滤器 和 基于文件的写入过滤器。因此,上述过程变得更加复杂
- 确保禁用 增强写入过滤器 和 基于文件的写入过滤器。
- 设置 Windows[tm] 应该使用的时区。
- 重新启动 Windows[tm] 并进入 CMOS 设置,将时钟设置为当前的 UTC 时间和日期
- 引导到 Windows[tm] 并(重新)启用 增强写入过滤器 和 基于文件的写入过滤器。
与外部链接 Computer Time Management and Embedded Systems (Windows Embedded Standard 7 Service Pack 1) 进行比较。