闰秒原理及其对计算机系统影响

(给ImportNew加星标,提高Java技能)


因为网上关于闰秒的原理和对系统影响描述不多且较模糊,因此写了此篇文章。 


说到闰秒我们首先明确三个概念,世界时、原子时和世界协调时 。


世界时(UT):可以简单理解为以地球自转为标准的计时。


原子时(TAI):国际原子时。采用基于铯原子(Cs 132.9)的能级跃迁原子秒作为时标。


原理就是,通过束缚态光子,影响原子核和其电子的耦合,产生能级跃迁。这种跃迁是根据光子环绕原子核的位置往复运动的。因此形成了稳定的间隔,而这种间隔就用来确定时间。大概是如下图所示:


世界协调时(UTC):这个缩写比较诡异,是不能正常语法拼写出来的。

英语:Coordinated Universal Time ,法语:Temps Universel Coordonné


当时对于英法都想自己的语言成为世界协调时的缩写,因此争论不下,最后大家各退一步使用了 UTC(Universal Time Coordinated)作为其缩写。


世界协调时简单的说,就是以 原子时 为计量单位,来表示世界时。 问题出现了,地球不是恒定转动的,而是越转越慢。当然这个慢是很细微的,不会是等几十年以后就不转了- -b


闰秒是一个一秒的调整,偶尔应用到协调世界时(UTC),以保持其时间接近平均太阳时间或世界时。没有这样的校正,地球旋转计算的时间会偏离原子时间。这个修正系统在1972年实施,已经插入了 27 个闰秒,最近在 2016 年 12 月 31 日 23:59:60 UTC,和下一个闰秒是否要加入尚在讨论当中。


具体来说,在所选择的 UTC 日期(一个月的最后一天,通常是 6 月 30 日或 12 月 31 日)的 23:59:59 和下一天的 00:00:00 之间插入正的闰秒。此闰秒在 UTC 时钟显示为 23:59:60。


负闰秒会在所选月份的最后一天的第二个 23:59:59,该日期的 23:59:58 将紧接在下一天的 00:00:00(负闰秒就很难见到啦,除非你现在开始从宇宙推地球让它转快点)。


这个是最近一个闰秒在www.time.gov 上的显示。



对于计算机来讲,我们如何同步到 UTC 时间呢?我们引进了 NTP 服务。


NTP(Network Time Protocol ):主要是用来同步网络上各个计算机上的时间。


看起来好像很简单,但是在现在这个把时间掰成秒的快节奏社会,几秒的误差都可能造成很严重的问题。就像警匪片里行动前都要对表,计算机之间的时间也要统一,不然就会造成你一个人冲进匪窝,面对各式长枪短炮,大家还在外面数秒的尴尬情况。


NTP 版本 0 是在1985年实现的,最早运用在了 UNIX 系统上。基本上是现存最老的互联网协议之一了。最近一次更新是 2010 年的版本 4。


下图的老人家就是 NTP 之父,因为 NTP 引起的各种系统问题请直接算他头上。



当然,他已经退休,在家颐养天年。


NTP 服务呢类似于如下层方式传递时间信息,黄线是直连的,红线走网络。



我们在系统中可以使用 ntpstat (UNIX like 系统)来看到时间源的层信息。理论上层数越低越好,但是考虑到网络因素的影响。也不绝对。最高 15 层,16 层默认是失败。



如果我们接入了互联网,我们可以通过互联网同步到时间源。


好了。下面我们谈谈闰秒对系统的影响。


从上面的知识我们得知,独立自己一台机器,在没有进行任何时间补丁更新的情况下是不会出现闰秒的。因为你根本接受不到 NTP 服务器发出的闰秒计划信息 - -b


一些组织报告了在 2012 年 6 月 30 日闰秒之后由有缺陷的软件引起的问题。在报告问题的网站有:


  • reddit(Apache Cassandra)

  • Mozilla(Hadoop)

  • Qantas Airways

  • 运行 Linux 的各种网站


  • 旧版本的摩托罗拉 Oncore VP、UT、GT 和 M12 GPS 接收器有一个软件错误,如果没有闰秒安排在 256 周,将导致一天的单一时间戳关闭。2003 年 11 月 28 日,这实际发生了。午夜时分,该固件的接收机报告 2003 年 11 月 29 日一秒钟,然后恢复到 2003 年 11 月 28 日。


    较老版本的 Trimble GPS 接收器具有软件缺陷,其将在 GPS 星座开始广播下一个闰秒插入时间(在实际闰秒之前几个月)时立即插入闰秒,而不是等待下一个闰秒发生。这使得接收者的时间在过渡期间暂停一秒钟。


    较老版本的基准 Tymeserve 2100 GPS 接收机和 Symmetricom Tymeserve 2100 接收机也有与老版本的 Trimble GPS 接收机类似的缺陷,时间关闭一秒钟。一旦接收到消息,就应用闰秒的提前通知,而不是等待正确的日期。有针对此问题的固件升级。已经描述和测试了解决方法,但如果 GPS 系统重播广播,或者设备关闭电源,则问题会再次发生。


    闰秒对商业部门的影响被描述为“一场噩梦”。由于金融市场容易受到技术和法律闰年问题的影响,洲际交易所母公司的 7 家结算公司和 11 家证券交易所,包括纽约证券交易所,在 2015 年 6 月 30 日的时候停止运营 61 分钟。


    Cisco NEXUS 5000 系列操作系统 NX-OS(版本 5.0、5.1、5.2)的几个版本受到影响。


    Google服务器不是在一天结束时插入闰秒,而是在闰秒之前在时间窗口上稍微延长秒数。因此没有受到影响。


    其他的东西我们接触到的机会并不多。


    主要对于系统运维的影响来自 Linux 服务器,Windows 不会受到任何系统级影响。


    Linux受影响的内核版本:


  • linux-2.6.22 之前内核版本

  • linux-2.6.25 到 2.6.27 内核版本

  • linux-3.4 内核版本

  • linux-2.6.32 内核版本


  • 查看内核版本命令


  • # uname -r


    我们说的是内核版本,因此全部使用 linux内核 的系统都会受到其影响。


    原理


    在标准的 UNIX 时间里会出现问题。


    这是由 CLOCK_REALTIME 系统时钟保持的时间,几乎每个需要知道当前时间的应用程序都会使用。它定义为 1970 年 1 月 1 日 00:00:00 UTC 后的秒数,但没有闰秒。系统时钟不能有时间 23:59:60,因为每分钟有 60 秒,每天有 86400 秒,这是系统定义死的。


    普通的计算机时钟不够稳定,不能保证时间精确到每年增加一秒或两秒。但是当一个时钟与 NTP 服务器以毫秒的精度同步时,时钟无法插入闰秒才是一个真正的问题。


    当闰秒插入 UTC 时,系统时钟跳过一秒,因为它不能被表示,并突然超过 UTC 一秒钟。有几种方式如何可以纠正时钟。


    1. 最常见的方法是当时钟到达 00:00:00 UTC 时,简单地将时钟返回一秒钟。


    这是在 Linux 内核中实现的。并且默认情况下,向后时间跳跃不是恰好在 00:00:00 发生,而是在第一次系统时钟更新。时钟重复大部分 23:59:59 秒,也是从 00:00:00 秒开始的一小部分。结果是时钟关闭一秒钟一秒钟。


    下图显示了 NTPf 服务如何在 2015 年 6 月 30 日使用默认配置更改的 UTC 时间的 TAI 偏移。



    这种调整方式我们称其为跳跃式调整。


    此时 Linux 系统会出现一句告警:


  • kernel: [xxxxxx.xxxxxx] Clock: inserting leap second 23:59:60 UTC


    闰秒代码从定时器那里中断处理程序, 每次时钟中断触发时会调用 tick_do_update_jiffies 更新 jiffies 的值。而 jiffies 主要记录了系统启动以来产生的处理频次,通过 jiffies/Hz 可以得到系统启动了多少秒。


    因此在这里需要对 xtime_lock 加入写锁


    闰秒代码调用 second_overflow 这个函数处理润秒, 我们截取这个函数确定 时间状态的一段判定。发现,


  • switch (time_state) {
    case TIME_OK: if (time_status & STA_INS) time_state = TIME_INS; else if (time_status & STA_DEL) time_state = TIME_DEL; break;
    case TIME_INS: if (xtime.tv_sec % 86400 == 0) { xtime.tv_sec--; wall_to_monotonic.tv_sec++; /* The timer interpolator will make time change gradually instead * of an immediate jump by one second. */ time_interpolator_update(-NSEC_PER_SEC); time_state = TIME_OOP; clock_was_set(); printk(KERN_NOTICE "Clock: inserting leap second 23:59:60 UTC\n"); } break;
    case TIME_DEL: if ((xtime.tv_sec + 1) % 86400 == 0) { xtime.tv_sec++; wall_to_monotonic.tv_sec--; /* Use of time interpolator for a gradual change of time */ time_interpolator_update(NSEC_PER_SEC); time_state = TIME_WAIT; clock_was_set(); printk(KERN_NOTICE "Clock: deleting leap second 23:59:59 UTC\n"); } break;
    case TIME_OOP: time_state = TIME_WAIT; break;case TIME_WAIT: if (!(time_status & (STA_INS | STA_DEL))) time_state = TIME_OK;}


    这里的 clock_was_set() 调用对 xtime_lock 的读锁,尴尬的是现在 xtime_lock 还处于锁状态,死锁发生。


    在之后的patch 里面 Thomas Gleixner 在函数里去掉了 clock_was_set() 函数,问题解决。


  • --- a/kernel/time/ntp.c+++ b/kernel/time/ntp.c@@ -122,7 +122,6 @@ void second_overflow(void)*/time_interpolator_update(-NSEC_PER_SEC);    time_state = TIME_OOP;- clock_was_set();  printk(KERN_NOTICE "Clock: inserting leap second ""23:59:60 UTC\n");}
    @@ -137,7 +136,6 @@ void second_overflow(void)*/ time_interpolator_update(NSEC_PER_SEC); time_state = TIME_WAIT;- clock_was_set();  printk(KERN_NOTICE "Clock: deleting leap second "  "23:59:59 UTC\n");}

    解决办法。


    方法 1


    升级内核到高版本。避开以上可能出现问题的版本,一劳永逸。


    但是毋庸置疑,升级内核这种方法可能会带来一系列的问题。特别是升级内核后要重启服务器,有可能升级过程中遇到莫名的问题。生产系统没有必要冒这么大的风险。


    方法 2

    因为单机并不受影响,因此,我们只需要在闰秒发生的前一天开始将 NTP 服务停掉。 为什么是前 1 天,因为闰秒通知和闰秒处理不是同时进行处理的。不是前 1 分钟关上就可以的。


  • # service ntpd stop

    过了闰秒的时间再开开。


  • # service ntpd start


    方法 3:特殊情况使用


    将 NTP 服务设置为 slew 模式。


    原理为:slew 模式并不使用跳跃式修改时间。比如一次调整 1 秒,而是每秒调整 0.5ms 来缓慢修正时间。这样就不会调用郁闷的 TIME_INS ,也就不会调用 clock_was_set() 产生锁冲突了。


    当然这个模式不是很好,因为调整的太慢了。差 1 秒还好。差几十秒能调整出几天去。在过了闰秒,调回来就好。


    设置方法


    修改 /etc/sysconfig/ntpd 文件。在 OPTIONS="-g" 前加上 -x 重启服务。


    注意 ,如果是做了集群请慎重。


    转自:ADCC千月,

    链接:blog.csdn.net/syshzbtt/article/details/53751560


    - EOF -

    推荐阅读   点击标题可跳转

    1、数据库日期类型字段设计,应该如何选择?

    2、计算机时间到底是怎么来的?程序员必看的时间知识!

    3、MySQL 由于 Java 日期 LocalDateTime 数据精度引发的线上问题


    看完本文有收获?请转发分享给更多人

    关注「ImportNew」,提升Java技能

    点赞和在看就是最大的支持❤️