![UNIX 时间是在运行 UNIX 的计算机上测量的。](https://linux22.com/image/90051/UNIX%20%E6%97%B6%E9%97%B4%E6%98%AF%E5%9C%A8%E8%BF%90%E8%A1%8C%20UNIX%20%E7%9A%84%E8%AE%A1%E7%AE%97%E6%9C%BA%E4%B8%8A%E6%B5%8B%E9%87%8F%E7%9A%84%E3%80%82.png)
免责声明:
我刚刚浏览了 StackExchange 站点列表大约 20 分钟,试图找出在哪里发布此内容。如果您知道任何更合适的网站,请将此问题移至那里。我在这里发布这个是因为 Unix 时间让我思考。
所以众所周知,有unix时间,也有UTC。 Unix 时间只是不断地滴答作响,以秒为单位——每秒一秒——而 UTC 则试图以我们使用的人类可读格式来保持时间与地球自转的相位一致。为此,UTC 会不时插入闰秒。
由于时间与经历时间的物体所受到的重力、其他类型的加速度和相对速度有关,这导致了两个问题。让我们先解决一个简单的问题:unix 时间是在哪里测量的?如果 Alice 和 Bob 一开始就同意当前时间为 1467932496.42732894722748,此时他们位于同一位置(一秒当然定义为 9'192'631'770 个辐射周期,对应于铯 133 的两个能级之间的跃迁)原子处于静止状态和 0 K 时),由于爱丽丝生活在海平面而鲍勃生活在高山上,或者爱丽丝生活在北极而鲍勃生活在赤道,因此遇到了孪生悖论,他们将不再同意。那么unix时间是如何精确定义的呢?
一开始你可能看不到 UTC 的问题,因为肯定每个人都同意地球何时完成一个轨道(这当然忽略了大陆板块的运动,但我认为我们很好地解决了这个问题,因为使用 GPS 可以测量它们的运动非常精确,我们可以假设它们位于模型中的固定位置,并且不会随着大陆板块的移动而移动),无论它们是在山上、在海平面上、在赤道上还是在北极上。可能存在一些时间差异,但它们不会累积。
但第二个被定义为 9'192'631'770 个辐射周期,对应于铯 133 原子在静止和 0 K 的两个能级之间的跃迁,并且铯 133 原子不关心地球轨道。因此,UTC 决定在哪里插入闰秒,但地球轨道相位和测量时间之间必须存在测量或预测的偏移某处通过原子钟。那是在什么地方?
答案1
你的标题问题没有真正的答案; Unix 时间不是真正的时间刻度,也不是在任何地方“测量”的。它是 UTC 的一种表示,尽管效果很差,因为 UTC 中的某些时刻是它无法表示的。 Unix 时间坚持每天有 86,400 秒,但 UTC 由于闰秒而与此有所偏差。
至于您更广泛的问题,有四个重要的时间范围值得关注:
UT1(世界时),由世界各地的天文台计算得出,这些天文台测量地球相对于恒星的旋转。通过这些观察和一些数学计算,我们得到了旧格林威治标准时间的更现代版本,它是基于格林威治皇家天文台的太阳正午时刻。世界时是由一个名为“世界时间”的组织计算的IERS(国际地球自转和参考系统服务处,前身为国际地球自转服务处)。
泰安(国际原子时),由世界各地数百个原子钟保存,由国家标准机构等维护。为 TAI 使用做出贡献的时钟守护者时间转移引导时钟相互靠近的技术,消除各个时钟的任何小误差并创建整体时间;该集合就是 TAI,由国际度量衡局 (BIPM)(SI 单位制的管理者)发布。为了回答你关于时间膨胀的问题,TAI 被定义为原子时间在海平面(实际上,在大地水准面,这是同一想法的更高级版本),每个时钟都会校正其自身高度的影响。
世界标准时间(协调世界时),于 1972 年 1 月 1 日设置为比 TAI 慢 10 秒,自该日期起,它以与 TAI 完全相同的速率向前移动,除非添加或减去闰秒。 IERS 决定宣布闰秒,以便将差异保持在 0.9 秒以内(实际上,大约在 0.6 秒内;增加闰秒会导致差异从 -0.6 变为 +0.4)。理论上,闰秒既可以是正的,也可以是负的,但由于与 SI 和 TAI 制定的标准相比,地球自转速度正在减慢,负闰秒从来没有必要,而且可能永远也不会。
Unix时间,它尽力将 UTC 表示为单个数字。每个 86,400 倍数的 Unix 时间都对应于 UTC 午夜。由于并非所有 UTC 日都长 86,400 秒,但所有“Unix 日”都是如此,因此存在不可调和的差异,必须以某种方式进行修补。没有与添加的闰秒相对应的 Unix 时间。在实践中,系统要么表现得好像前一秒发生了两次(unix 时间戳向后跳一秒,然后再次向前前进),要么应用类似的技术跳跃涂抹这会在闰秒的两侧扭曲时间更长的时间。无论哪种情况都存在一些不准确性,尽管至少第二种情况是单调的。在这两种情况下,两个遥远的 Unix 时间戳之间经过的时间量A和乙不等于乙-A;它等于乙-A 加上中间的闰秒数。
由于 UT1、TAI、UTC 和 IERS 都是全球范围内的跨国工作,因此没有单一的“地点”,尽管 IERS 公报是从巴黎天文台发布的,BIPM 也位于巴黎,这就是一个答案。需要精确、可追踪时间的组织可能会将其时基声明为“UTC(USNO)”之类的内容,这意味着它们的时间戳采用 UTC,并且源自美国海军天文台的时间,但考虑到以下问题:我提到过 Unix 时间,它基本上与这种精度水平不兼容——任何处理真正精确时间的人都会有 Unix 时间的替代品。
答案2
UNIX 时间是在运行 UNIX 的计算机上测量的。
这个答案希望您知道什么是协调世界时 (UTC)、国际原子时 (TAI) 和国际单位制秒。对它们的解释是远远超出Unix 和 Linux Stack Exchange 的范围。 这不是物理学或天文学堆栈交换。
硬件
您的计算机包含驱动时钟和定时器的各种振荡器。具体内容因计算机而异,具体取决于其体系结构。但通常,笼统地说:
- 有一个可编程间隔定时器(PIT)某处,可以对其进行编程以计算给定数量的振荡并触发中央处理单元的中断。
- 有一个周期计数器在中央处理器上,每个执行的指令周期简单地计数 1。
广义上的操作理论
操作系统内核利用PIT来生成蜱虫。它将 PIT 设置为自由运行,在百分之一秒的时间间隔内计算正确的振荡次数,生成中断,然后自动重置计数以再次进行。这方面有一些变化,但本质上这会导致打钩以固定频率引发中断。
在软件中,内核每个时钟周期都会增加一个计数器。它知道滴答频率,因为它首先对 PIT 进行了编程。所以它知道一秒有多少个滴答声。它可以使用它来知道何时增加计算秒数的计数器。后者就是内核的“UNIX Time”的想法。事实上,如果留给它自己的设备的话,它确实只是以每 SI 每秒 1 的速率向上计数。
有四件事使这个问题变得复杂,我将用非常笼统的术语来介绍它们。
硬件并不完美。 PIT 的数据表显示其振荡器频率为氮赫兹的频率可能是(比如说)氮.00002 赫兹,后果显而易见。
该方案与电源管理的互操作性非常差,因为 CPU 每秒唤醒数百次,除了增加变量中的数字之外几乎没有什么作用。因此,某些操作系统具有所谓的“无滴答”设计。内核不是让 PIT 为每个时钟周期发送一个中断,而是计算出(从低级调度程序)在没有线程量子耗尽的情况下将经过多少个时钟周期,并对 PIT 进行编程以将这些时钟周期计数到future 在发出滴答中断之前。它知道它必须记录通过氮在下一个滴答中断处滴答,而不是 1 个滴答。
应用软件能够改变内核的当前时间。它可以步值或者它可以屠杀价值。回转涉及调整增加秒计数器所需的滴答数。因此秒计数器不一定以每 SI 秒 1 的速率计数反正,即使假设完美的振荡器。步进只需在秒计数器中写入一个新数字,这通常要等到上一秒结束后 1 SI 秒才会发生。
现代内核不仅计算秒,还计算纳秒。但每纳秒一次的滴答中断是荒谬的,而且通常是完全不可行的。这就是类似的地方周期计数器参加进来。内核会记住每秒(或每个时钟周期)的周期计数器值,并可以根据当前的当想要知道以纳秒为单位的时间时计数器的值,自上一秒(或滴答)以来必须经过多少纳秒。不过,电源和热管理再次对此造成严重破坏,因为指令周期频率可能会发生变化,因此内核会依赖额外的硬件,例如高精度事件定时器(HPET)。
C语言和POSIX
C 语言的标准库用不透明类型 、具有各种指定字段的time_t
结构类型tm
以及各种库函数(如time()
、mktime()
和 )来描述时间localtime()
。
简言之:C语言本身仅保证这time_t
是可用的之一数字数据类型,并且计算时间差的唯一可靠方法是函数difftime()
。 POSIX 标准提供了更严格的保证,这time_t
实际上是其中之一整数类型及其计数自纪元以来的秒数。它也是指定timespec
结构类型的 POSIX 标准。
该time()
函数有时被描述为系统调用。事实上,现在它已经很长时间没有成为许多系统上的底层系统调用了。例如,在 FreeBSD 上,底层系统调用是clock_gettime()
,它具有各种可用的“时钟”,可以以各种方式以秒或秒+纳秒为单位进行测量。应用程序软件正是通过这个系统调用从内核读取 UNIX 时间。 (匹配的clock_settime()
系统调用允许他们步进它,adjtime()
系统调用允许他们杀死它。)
许多人都在使用 POSIX 标准,并对其规定的内容提出非常明确和准确的声明。这些人往往并没有真正读POSIX 标准。正如其基本原理所述,计算“自纪元以来的秒数”的想法(该标准使用的短语),故意地没有指定 POSIX 秒与 SI 秒的长度相同,也没有指定 的结果gmtime()
“必然是 UTC,尽管它的出现”。 POSIX 标准是故意地足够宽松,以便允许管理员在 UNIX 系统中手动修复闰秒调整,方法是在闰秒调整发生一周后重新设置时钟。事实上,基本原理指出,它故意足够宽松,以适应时钟的系统被故意设置错误当前 UTC 时间以外的某个时间。
UTC 和 TAI
从内核获取的 UNIX 时间的解释取决于应用程序中运行的库例程。 POSIX 指定内核时间和struct tm
.但是,正如丹尼尔·J·伯恩斯坦(Daniel J. Bernstein)曾经指出的那样,1997 年版的标准在这一认定上犯了令人尴尬的错误,搞乱了公历的闰年规则(学童们学过的东西),因此从 2100 年开始计算就出现了错误。 “违反比遵守更受尊敬”这句话很容易浮现在脑海中。
确实如此。如今,有几个系统将此解释基于 Arthur David Olson 编写的库例程,这些例程参考臭名昭著的“Olson 时区数据库”,通常编码在/usr/share/zoneinfo/
. Olson 系统有两种模式:
- 内核的“自纪元以来的秒数”被认为是计算自 1970-01-01 00:00:00 UTC 以来的 UTC 秒数,闰秒除外。这使用
posix/
Olson 时区数据库文件集。所有天都有 86400 内核秒,并且一分钟不会有 61 秒,但它们并不总是 SI 秒的长度,并且当闰秒发生时,内核时钟需要旋转或步进。 - 内核的“自纪元以来的秒数”被认为是计算自 1970-01-01 00:00:10 TAI 以来的 TAI 秒数。这使用
right/
Olson 时区数据库文件集。内核秒的长度为 1 SI 秒,并且内核时钟从不需要旋转或步进来调整闰秒,但细分时间可以具有诸如 23:59:60 之类的值,并且天数并不总是 86400 内核秒长。
M. Bernstein 编写了多个工具,包括他的daemontools
工具集,之所以需要这些工具,是right/
因为它们只需添加 10 即可time_t
获得自 1970-01-01 00:00:00 TAI 以来的 TAI 秒数。他在手册页中记录了这一点。
这一要求(也许是在不知不觉中)被Felix von Leitner 的daemontools-encore
和等工具集继承了。使用runit
libowfat
伯恩斯坦multilog
,冈特multilog
, 或者帕普svlogd
例如,使用 Olsonposix/
配置,所有 TAI64N 时间戳将(在撰写本文时)落后 26 秒实际的TAI 自 1970-01-01 00:00:10 TAI 以来的秒计数。
Laurent Bercot 和我在 s6 和 nosh 中解决了这个问题,尽管我们采取了不同的方法。 M.贝尔科特的tai_from_sysclock()
依赖于编译时标志。处理 TAI64N 的 nosh 工具会查看TZ
和TZDIR
环境变量以自动检测posix/
以及right/
是否可以。
有趣的是,FreeBSD 的文档time2posix()
和posix2time()
函数允许相当于 Olsonright/
模式的time_t
TAI 秒。然而,它们显然并未启用。
再次…
UNIX 时间是在运行 UNIX 的计算机上通过计算机硬件中包含的振荡器来测量的。它不使用 SI 秒;它不是 UTC,尽管它表面上可能很相似;和它故意地允许你的时钟出错。
进一步阅读
- 丹尼尔·J·伯恩斯坦。UTC、TAI 和 UNIX 时间。 cr.yp.to。
- 丹尼尔·J·伯恩斯坦。
tai64nlocal
。守护进程工具。 cr.yp.to。 - ”自纪元以来的秒数”。 单一 UNIX 规范版本 2。 1997. 公开组。
- ”自纪元以来的秒数(基本原理)”。 基本规范第 6 期。 2004。IEEE 1003.1。开放组。
- 大卫·马多雷 (2010-12-17)。 Unix 闰秒混乱。
time2posix
。 FreeBSD 10.3 手册。 § 3.- https://physical.stackexchange.com/questions/45739/
- https://astronomy.stackexchange.com/questions/11840/