闰秒终于要取消了!一文详解其来源及影响

导读 | 第27届世界计量大会宣告最迟不晚于2035年撤销引进闰秒,这一消息引起轰动。上一次闰秒发生,对Reddit、Mozilla、FourSquare等都发生了一定的问题,其间Reddit宕机时刻超过1个半小时!本栏目特邀腾讯后台开发工程师陶松桥,带你是深入了解闰秒的来历及其影响,并介绍各类体系常见的闰秒处理办法,其间会分享TencentOS Server 操作体系的处理方案。

闰秒终于要取消了!一文详解其来源及影响

闰秒从何而来

世界上有几种计量时刻的办法:

世界时(UT1):是一种天文计量的办法,天文学家通过观测地球的自转,并将自转一周的时刻(一天)等分为86400份,每份为一秒,受潮汐等要素的影响,地球自转一周的时刻并不是安稳的,这也是形成闰秒现象的直接原因。

原子时(TAI):由于上面描绘的世界时并不安稳,物理学家用更为安稳的量子计量的办法来统计时刻,1967年,世界计量大会用铯133(Cs133)原子基态的两个超精密能级之间的跃迁所对应辐射的9192631770个周期所持续的时刻定义为1秒,这个是现在最准确的时刻计量办法,其误差为1400000年一秒,根本可忽略不计。

和谐世界时(UTC):又称世界规范时刻或世界和谐时刻,UTC以TAI为基础,又要统筹UT1,当UTC,和UT1之间的误差挨近1秒时,世界地球自转和参考系服务(IERS)会提早6个月发布下一次闰秒的时刻。

咱们将世界绝大多数当地时区的根本时刻称为和谐世界时,即 UTC。它源自散布在世界一些国家的大量原子时钟。地球的自转并不是非常安稳的,有时会有一些变化,平均自转速度会缓慢下降。这便是为什么会在 UTC 时标中刺进所谓闰秒,它们可将 UTC 时刻进程调整到实在地球自转时刻。

为什么会多出这一秒呢?它的存在是由于抉择昼夜替换的地球绕轴自转会在很长的一段时刻内慢下来,主要是由月亮-太阳引力形成。别的,地球也受其内部(地核、地幔)和外部(气候、海洋)构成影响。现在,时刻主要由分属几个国家的 250台原子时钟丈量,这些原子钟是通过丈量原子的能量转化水平作业。运用这些时钟计算 UTC,一起由于这个时刻丈量原理周期性地与地球不同步,因而有必要运用闰秒进行校对。别的,咱们有必要考虑到现在的一天比 1820年的一天要长 2 毫秒。不出所料,地球自转渐渐就与 UTC 不同步了。

世界地球自转服务局IERS 丈量的是实在地球自转,并抉择何时刺进闰秒。刺进闰秒一般总是在某个月的最后一天进行,首选六月或十二月的 UTC 午夜时刻。曩昔每次增加闰秒都是在六月或十二月进行。是否增加闰秒的声明,会由 IERS 在其Bulletin C 中发布。现在,在或许增加闰秒日期前半年会发布 Bulletin C 。

由于闰秒是在全世界一起刺进,刺进闰秒的本地(民用)时刻取决于本地时刻与 UTC 之间的误差,例如:2015年7月1日发生闰秒时,在时区 UTC+8h(北京时刻)中,闰秒会在时钟显现午夜后 8 小时的时候刺进。

闰秒时计算北京时刻的规范办法为:

2015-07-01 07.59.57
2015-07-01 07.59.58
2015-07-01 07.59.59
2015-07-01 07.59.60 <-- 闰秒
2015-07-01 08.00.00
2015-07-01 08.00.01
2015-07-01 08.00.02

**如果体系时钟选用世界原子时(TAI),并运用正确的时区,那么就会列出 23:59:60。**但由于在 Unix 的 UTC 运用中不存在 23:59:60,Linux内核会选用倒回一秒的办法在0:00 UTC 后第一次时钟更新时刺进闰秒。在本地时刻计时中,依据不同的时区误差,比如 UTC+8h,在TencentOS Server体系中,您会观察到以下现象:

2015-07-01  07:59:58.000
2015-07-01  07:59:58.500
2015-07-01  07:59:59.000
2015-07-01  07:59:59.500
2015-07-01  08:00:00.000 <-- 刺进闰秒
2015-07-01  07:59:59.000
2015-07-01  07:59:59.500
2015-07-01  08:00:00.000
2015-07-01  08:00:00.500

IERS 确认闰秒后,一些时刻传达服务还会发布闰秒告诉。这包含德国长波发射机 DCF77 和卫星巡航体系 GPS 示例。因而,可解码从那些体系获取信号的接收器也能够解码闰秒告诉。如果在所应用协议中包含闰秒信息(例如接收器传送的时刻字符串),则从那些接收器读取时刻的应用程序也能够确认闰秒告诉。请注意,时刻代码接收器只能将闰秒告诉转发到应用程序,一起正确计时。正确处理闰秒是应用程序和(/或)操作体系的使命。

从1972年到2020年,平均每21个月就刺进一次闰秒。可是,间隔是非常不规则的,并且明显在增加。在1999年1月1日至2004年12月31日的六年中没有闰秒,但在1972-1979年的八年中有九个闰秒。自1972年和谐世界时正式运用至今,全球现已施行了27次正闰秒调整,最近一次的闰秒调整是格林尼治时刻2016年12月31日。从和谐世界时正式运用以来,地球自转一向处于不断减慢的趋势,因而迄今为止的闰秒都是正闰秒。但相关科研发现,自2020年年中以来,地球自转速率呈现加速趋势,这意味着未来也或许会呈现负闰秒现在TAI与UTC的秒差为37:

#  Value of TAI-UTC in second valid beetween the initial value until
#  the epoch given on the next line. The last line reads that NO
#  leap second was introduced since the corresponding date 
#  Updated through IERS Bulletin 64 issued in July 2022
#  
#
#  File expires on 28 June 2023
#
#
#    MJD        Date        TAI-UTC (s)
#           day month year
#    ---    --------------   ------   
#
    41317.0    1  1 1972       10
    41499.0    1  7 1972       11
    41683.0    1  1 1973       12
    42048.0    1  1 1974       13
    42413.0    1  1 1975       14
    42778.0    1  1 1976       15
    43144.0    1  1 1977       16
    43509.0    1  1 1978       17
    43874.0    1  1 1979       18
    44239.0    1  1 1980       19
    44786.0    1  7 1981       20
    45151.0    1  7 1982       21
    45516.0    1  7 1983       22
    46247.0    1  7 1985       23
    47161.0    1  1 1988       24
    47892.0    1  1 1990       25
    48257.0    1  1 1991       26
    48804.0    1  7 1992       27
    49169.0    1  7 1993       28
    49534.0    1  7 1994       29
    50083.0    1  1 1996       30
    50630.0    1  7 1997       31
    51179.0    1  1 1999       32
    53736.0    1  1 2006       33
    54832.0    1  1 2009       34
    56109.0    1  7 2012       35
    57204.0    1  7 2015       36
    57754.0    1  1 2017       37

闰秒终于要取消了!一文详解其来源及影响
闰秒处理方案

1)运转NTP的体系

体系如果运用 NTP(网络时刻协议)看护进程(ntpd)将其本地计时与 NTP 服务器同步,则都应主动进行闰秒调整。进行闰秒调整的前一天,NTP 服务器应告诉其客户端第二天的 23:59:59 UTC 会产出发生闰秒,Linux 内核应通过两次显现第 60秒或完全删去它,以便增加或许删去额定一秒。因而,在闰秒调整期间,运转 NTP 的体系应有如下计时显现:

2015-06-30 23:59:59 UTC
2015-06-30 23:59:59 UTC
2015-06-30 00:00:00 UTC

发生闰秒时,内核会在体系 log 中写入信息:

Jul  1 07:59:59 TENCENT64 kernel: [579201.951291] Clock: inserting leap second 23:59:60 UTC

运用ntpdate指令办法,与ntp服务器进行时刻同步的体系,将不会通过ntp服务器接收到闰秒告诉,而是在体系管理员指定的时刻与ntp服务器进行时刻同步。例如,体系管理员设定每小时的第52分与ntp服务器进行时刻同步,那么在7月1日08:00 CST到09:52之间,体系时刻与ntp服务器时刻会相差1秒(快1秒)。

2)运转PTP的体系

PTP(准确时刻协议)中交换的时刻戳通常选用不包含闰秒的TAI(世界原子时);但 ptp4l 和 phc2sys 将设置内核标签,刺进闰秒以便体系时钟持续以 UTC 运转。然后该内核就能够正常刺进闰秒。

3)未运转NTP或许PTP的体系

默认情况下,不运用 NTP 或许 PTP 同步其计时的 Linux 体系不会批改闰秒,且这些体系陈述的时刻与批改闰秒后的 UTC 时刻有一秒钟的不同。闰秒发生后应手动重置时钟。

您还能够将 tzdata 更新至最新版别,将/usr/share/zoneinfo/right 目录层级中的正确文件复制到/etc/localtime,并将时钟重置到正确的本地时刻,以便将这些体系配置可正确陈述时刻。/usr/share/zoneinfo/right 中的文件包含自该世纪开端,从 1970年 1 月 1 日00:00:00 UTC 发生的所有闰秒批改的本地时刻信息。/usr/share/zoneinfo 中的其他时区文件未增加闰秒批改。从1972年至今,共增加了 27次闰秒。

例如:如果某个体系位于中国时区,您能够将其重新配置为通过运转以下指令陈述闰秒批改时刻,

cp /usr/share/zoneinfo/right/Asia/Shanghai /etc/localtime

例如在TS2体系中,tzdata包的版别为tzdata-2015a-1.tl2.noarch,执行完上述拷贝后,则会在闰秒发生时刻2015年7月1日8点主动刺进闰秒。

4)windows体系

早期的Windows版别(Win10版别曾经)时刻服务并不表明 Leap 指标的值,当 Windows 时刻服务接收到的数据包,包含闰秒。因而,闰秒发生后,正在运转 Windows 时刻服务的 NTP 客户端会比实践时刻快一秒。这种时刻差异在下次同步时处理。

从 Windows 10 Redstone 5 和 Windows Server 2019 起,微软的操作体系能以更准确、UTC 兼容和可追踪的办法处理闰秒。不过从2017年至今,没有发生过闰秒了。

闰秒终于要取消了!一文详解其来源及影响
历史影响

关于日常日子而言,正常的上班、下班、作业、学习,生命中误差的这一秒无关痛痒。可是闰秒关于准确要求时刻的职业如航空、航天、军工等,会发生较大影响。**关于服务器清一色linux体系的互联网职业而言,闰秒或许会形成机器cpu突然增高,机器宕机、对应的服务挂掉。**随着linux的普遍运用,闰秒的影响也被越来越多的被关注。

历史上,由于linux内核的一些问题,闰秒对体系形成多次影响。比如CPU利用率高会给出产环境带了不少挑战。2012年施行闰秒时,国外不少闻名网站呈现了暂时服务中止。当2015年闰秒再度来暂时,工程师们批改了部分2012年呈现的问题,但却东窗事发——发现了新的问题。后续亦是如此。闰秒让互联网企业如鲠在喉。

1) linux-2.6.22曾经内核版别的闰秒死锁

07年的commit:

git.kernel.org/?p=linux/ke…

每次时钟中止触发时会调用 tick_do_update_jiffies64 更新 jiffies 的值。因而在更新前对 xtime_lock 加了写锁。闰秒发生时,开发者需求批改jiffies 的值。在 tick_do_update_jiffies64 里边最终会调用到 second_overflow 这个函数,以处理润秒。在函数 second_overflow 里边,处理润秒的增加和削减前都调用了一个 clock_was_set 函数。该函数内部,请求了 xtime_lock 的读锁。此时,与先前的写锁发生死锁。

该patch在linux内核版别2.6.22中引进,所以只有2.6.22内核之前的体系可会呈现该问题,也便是影响sles10和centos5.5体系。在sles10和centos5.5中,clock_was_set()因不支持高精度时钟而被定义为空,所以不形成影响。

2)linux-2.6.25到2.6.27内核版别的体系死锁

Bug 479765 – Leap second message can hang the kernel 描绘了leap second会对体系发生影响的原因:

当一个leap second被刺进或删去时,内核会打印一条相关信息:

[69596.647516] Clock: inserting leap second 23:59:60 UTC

而该信息的打印会因xtime_lock而形成体系死锁。

下面是2.6.26内核下该问题呈现时的栈信息(this is with Fedora 8 and

kernel kernel-2.6.26.6-49.fc8.x86_64):

#0 ktime_get_ts (ts=0xffffffff8158bb30) at include/asm/processor.h:691
#1 0xffffffff8104c09a in ktime_get () at kernel/hrtimer.c:59
#2 0xffffffff8102a39a in hrtick_start_fair (rq=0xffff810009013880,
 p=<value optimized out>) at kernel/sched.c:1064
#3 0xffffffff8102decc in enqueue_task_fair (rq=0xffff810009013880,
 p=0xffff81003fb02d40, wakeup=1) at kernel/sched_fair.c:863
#4 0xffffffff81029a08 in enqueue_task (rq=0xffffffff8158bb30,
 p=0xffff81003b8ac418, wakeup=-994836480) at kernel/sched.c:1550
#5 0xffffffff81029a39 in activate_task (rq=0xffff810009013880,
 p=0xffff81003b8ac418, wakeup=20045) at kernel/sched.c:1614
#6 0xffffffff8102be38 in try_to_wake_up (p=0xffff81003fb02d40,
 state=<value optimized out>, sync=0) at kernel/sched.c:2173
#7 0xffffffff8102be9c in default_wake_function (curr=<value optimized out>,
 mode=998949912, sync=20045, key=0x4c4b40000) at kernel/sched.c:4366
#8 0xffffffff810492ed in autoremove_wake_function (wait=0xffffffff8158bb30,
 mode=998949912, sync=20045, key=0x4c4b40000) at kernel/wait.c:132
#9 0xffffffff810296a2 in __wake_up_common (q=0xffffffff813d3180, mode=1,
 nr_exclusive=1, sync=0, key=0x0) at kernel/sched.c:4387
#10 0xffffffff8102b97b in __wake_up (q=0xffffffff813d3180, mode=1,
 nr_exclusive=1, key=0x0) at kernel/sched.c:4406
#11 0xffffffff8103692f in wake_up_klogd () at kernel/printk.c:1005
#12 0xffffffff81036abb in release_console_sem () at kernel/printk.c:1051
#13 0xffffffff81036fd1 in vprintk (fmt=<value optimized out>,
 args=<value optimized out>) at kernel/printk.c:789
#14 0xffffffff81037081 in printk (
 fmt=0xffffffff8158bb30 "yj$\201????\2008\001\t") at kernel/printk.c:613
#15 0xffffffff8104ec16 in ntp_leap_second (timer=<value optimized out>)
 at kernel/time/ntp.c:143
#16 0xffffffff8104b7a6 in run_hrtimer_pending (cpu_base=0xffff81000900f740)
 at kernel/hrtimer.c:1204
#17 0xffffffff8104b86a in run_hrtimer_softirq (h=<value optimized out>)
 at kernel/hrtimer.c:1355
#18 0xffffffff8103b31f in __do_softirq () at kernel/softirq.c:234
#19 0xffffffff8100d52c in call_softirq () at include/asm/current_64.h:10
#20 0xffffffff8100ed5e in do_softirq () at arch/x86/kernel/irq_64.c:262
#21 0xffffffff8103b280 in irq_exit () at kernel/softirq.c:310
#22 0xffffffff8101b0fe in smp_apic_timer_interrupt (regs=<value optimized out>)
 at arch/x86/kernel/apic_64.c:514
#23 0xffffffff8100cf52 in apic_timer_interrupt ()
 at include/asm/current_64.h:10
#24 0xffff81003b9d5a90 in ?? ()
#25 0x0000000000000000 in ?? ()

从上面的栈信息咱们能够发现:该问题的呈现原因是当对leap second进行操作(刺进或删去)之前,现已获取了xtime_lock锁;而之后在调用printk()打印日志信息时,printk()中会尝试唤醒klogd内核线程,在唤醒过程中会调用到公平调度类的相关函数,其间会调用ktime_get()获取时刻信息,其间会再次尝试获取xtime_lock锁,从而形成死锁

该现象部分由于hrtick_start_fair()函数的引进。是由commit 8f4d37ec (high-res preemption tick)引发,这大概在2.6.25版别引进。可是在2.6.25之前的内核,不会发生这个死锁。

2.6.28版别引进了commit b845b517。printk()中的wake_up_klogd()不会直接wake_up klogd(),也就不会触发后续的xtime_lock,最终避免了死锁的发生。所以,该原因引起的体系死锁只或许发生在linux内核2.6.25到2.6.27版别下

Sles11运用2.6.27内核,归于比较危险的部分内核。可是Novell声称现已引进了commit b845b517b5e3706a3729f6ea83b88ab85f0725b0,因而不存在该问题,并且几个小时的实验后体系仍然正常。

此问题影响的版别还有 RHEL4:kernel-2.6.9.89.EL之前的版别,RHEL5.3:kernel-2.6.18-128.37.1.el5之前的版别。现网centos5.5运用的内核版别是2.6.18-194.el5,其不受影响。

3)linux-3.4内核版别的体系活锁

08年的commit中为了处理之前遇到的leap second问题而将对leap second的处理从second_overflow()中独立出来,运用定时器来完成此作业。

可是12年的commit以为该patch存在如下或许的livelock场景:

CPU 0 CPU 1
do_adjtimex()
spin_lock_irq(&ntp_lock);
process_adjtimex_modes(); timer_interrupt()
process_adj_status(); do_timer()
ntp_start_leap_timer(); write_lock(&xtime_lock);
hrtimer_start();  update_wall_time();
hrtimer_reprogram(); ntp_tick_length()
tick_program_event() spin_lock(&ntp_lock);
clockevents_program_event()
ktime_get()
seq = req_seqbegin(xtime_lock);

问题在于,引进ntp_lock的commit(patches.linaro.org/5122/)

是在3.4内核版别,且在3.4内核得到了批改。所以此问题对3.4曾经和今后的内核无影响。

08年的commit:git.kernel.org/cgit/linux/…

12年的commit:

lkml.org/lkml/2012/3…

4)linux-2.6.32内核刺进闰秒或许呈现高CPU耗费

2012年的闰秒刺进其时导致了一些互联网公司的服务器高cpu耗费,其问题本源在以下网址得到了论述:lkml.org/lkml/2012/7…

leap-a-day.c为一个小测验程序,编译后加-s参数运转,可每10秒刺进或许删去一个闰秒,用户可自行下载编译测验。2015年7月1日的闰秒将会呈现以下现象:

Setting time to Wed Jul 1 07:59:50 2015
Scheduling leap second for Wed Jul 1 08:00:00 2015
Wed Jul 1 07:59:57 2015 + 98 us (3883) TIME_INS
Wed Jul 1 07:59:57 2015 + 500248 us (3883) TIME_INS
Wed Jul 1 07:59:58 2015 + 366 us (3883) TIME_INS
Wed Jul 1 07:59:58 2015 + 500483 us (3883) TIME_INS
Wed Jul 1 07:59:59 2015 + 598 us (3883) TIME_INS
Wed Jul 1 07:59:59 2015 + 500740 us (3883) TIME_INS
Wed Jul 1 07:59:59 2015 + 910 us (3883) TIME_OOP
Wed Jul 1 07:59:59 2015 + 501046 us (3883) TIME_OOP
Wed Jul 1 08:00:00 2015 + 1214 us (3884) TIME_WAIT
Wed Jul 1 08:00:00 2015 + 501359 us (3884) TIME_WAIT
Wed Jul 1 08:00:01 2015 + 1481 us (3884) TIME_WAIT
Wed Jul 1 08:00:01 2015 + 501599 us (3884) TIME_WAIT
Wed Jul 1 08:00:02 2015 + 1650 us (3884) TIME_WAIT

咱们测验后发现,在TS1.2发行版下,可呈现“ERROR: hrtimer early expiration failure observed”提示。

/* Test for known hrtimer failure */
void test_hrtimer_failure(void)
{
 struct timespec now, target;
 clock_gettime(CLOCK_REALTIME, &now);
 target = timespec_add(now, NSEC_PER_SEC/2);
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &target, NULL);
 clock_gettime(CLOCK_REALTIME, &now);

 if (!in_order(target, now)){
 printf("ERROR: hrtimer early expiration failure observed.\n");
 }

分析代码能够发现:运用clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &target, NULL);这种定时器办法,在刺进闰秒后,该定时器本应该0.5秒到期,却马上到期。实质原因是内核中记录时刻的数据结构中并没有表达闰秒的当地,因而在增加闰秒时需求特别调整这些数据结构。而许多定时器并不直接运用“绝对”时钟而运用相对的时刻间隔,这样,在定时器代码中就应该对闰秒做额定的检查。

但问题是这样的检查之前被删掉。**关于许多应用来说,定时器的一次提早触发并不是什么问题。但有些定时器则否则,他们会重复启动自己,这样的后果便是它们重复地被快速唤醒,所以体系负载就呈现了观察到的尖峰现象。**闰秒的刺进没有调用clock_was_set(),来提示hrtimer子体系改变。定时器在刺进闰秒后,其基准比体系时刻快一秒,因而会提早一秒到期。

在观察到cpu高耗费后,处理办法很简单,执行下述指令即可:

date -s "`date`"

其原理便是date再设置一下当前体系时刻,clock_settime(CLOCK_REALTIME,&ts)会调用clock_was_set()。为了应对ntpd同步或许呈现的该问题,咱们在2015年特意编写了一个处理程序,该程序通过编译后能够增加到crontab使命:

58 7 1 7 * /data/solve_hrtimer_failure.o > /data/solve_hrtimer_failure.log 2>&1

在7月1日7点58分开端,每隔100ms检测闰秒是否刺进了,当刺进闰秒后,该程序调用clock_settime函数,从而批改了该问题。

闰秒终于要取消了!一文详解其来源及影响
撤销闰秒

1)为何撤销闰秒

对闰秒最为灵敏的莫过于计算机相关范畴。**由于闰秒的呈现没有固定规律,对应的时刻调整无法从一开端就写在计算机程序里。**在万物互联年代,许多范畴都依托计算机网络传输信息,施行闰秒也会影响航空、通信、金融及其他需求精准对时的范畴。

今年7月Meta公司两名工程师发文称:“闰秒是一种弊大于利的冒险做法,咱们以为现在是时候引进新技术来取代它了。”这一表态引来各大公司称道。

2)撤销闰秒的后续或许

担任和谐世界时的世界计量局(BIPM)表明,科学家和政府代表18日在法国举行的一次会议上投票抉择到2035年撤销闰秒。BIPM时刻部分担任人帕特里齐亚塔维拉表明,这项“历史性抉择”将答应“秒数接连流动,而不会呈现现在由不规则闰秒形成的不接连性。

闰秒是现在把世界时和世界原子时联系起来的手段。由于世界时是依据地球自转确认的,又称天文时或太阳时。没有闰秒意味着人们运用的时刻与地球自转、太阳位置不关联,时刻和天文学呈现割裂状况。

第27届世界计量大会抉择要求多机构洽谈,提出一个能够将和谐世界时持续至少百年的新方案并拟定施行方案,纳入下一届大会的抉择草案中。依据抉择,闰秒将暂时持续正常增加。但到2035年,世界时和世界原子时之间的差异将被答应增加到大于一秒的值。

或许处理这个问题的或许办法是让世界时和世界原子时之间的差异增加到一分钟,但专家估计调整时长在50到100年之间。而有提议指出,无需在时钟上增加闰分钟,而是将某一天的最后一分钟变为需求两分钟;也有人主张停止校对,一起发布世界时和世界原子时之间不断增加的时刻差。

腾讯工程师技术干货直达:

1、算法工程师深度解构ChatGPT技术

2、10分钟!从架构视角读懂K8s

3、探秘微信事务优化:DDD从入门到实践

4、祖传代码重构:从25万行到5万行的血泪史

闰秒终于要取消了!一文详解其来源及影响

阅览原文