因为网上关于闰秒的原理和对系统影响描述不多且较模糊,因此写了此篇文章。
此文写了几个小时,转载请注明作者,感谢。
by 千月
说到闰秒我们首先明确3个概念,世界时,和原子时和世界协调时 。
世界时(UT):可以简单的理解为以地球自转为标准的计时。
原子时(TAI):国际原子时。采用基于铯原子(Cs 132.9)的能级跃迁原子秒作为时标。
原理就是,通过束缚态光子,影响原子核和其电子的耦合,产生能级跃迁。
这种跃迁是根据光子环绕原子核的位置往复运动的。
因此形成了稳定的间隔,而这种间隔就用来确定时间。大概是如下图所示。
世界协调时(UTC):这个缩写比较诡异,是不能正常语法拼写出来的。英语:Coordinated Universal Time ,法语:Temps Universel Coordonné当时对于英法都想自己的语言成为世界协调时的缩写,因此争论不下,最后大家各退一步使用了 UTC(Universal Time Coordinated)作为其缩写。世界协调时简单的说,就是以 原子时 为计量单位,来表示世界时。
问题出现了,地球不是恒定转动的,而是越转越慢。当然这个慢是很细微的,不会是等几十年以后就不转了。。。- -b
闰秒是一个一秒的调整,偶尔应用到协调世界时(UTC),以保持其时间接*均太阳时间或世界时。没有这样的校正,地球旋转计算的时间会偏离原子时间。
这个修正系统在1972年实施,已经插入了26个闰秒,最近在2015年6月30日在23:59:60 UTC,和下一个闰秒将插入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;elseif (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重启服务。注意 ,如果是做了集群。。请慎重。。。。