闰秒终于要取消了!一文详解其来源及影响
为什么会多出这一秒呢?它的存在是因为决定昼夜更替的地球绕轴自转会在很长的一段时间内慢下来,主要是由月亮-太阳引力造成。另外,地球也受其内部(地核、地幔)和外部(气候、海洋)构成影响。目前,时间主要由分属几个国家的 250 台原子时钟测量,这些原子钟是通过测量原子的能量转换水平工作。使用这些时钟计算 UTC,同时因为这个时间测量原理周期性地与地球不同步,因此必须使用闰秒进行校正。另外,我们必须考虑到现在的一天比 1820 年的一天要长 2 毫秒。不出所料,地球自转慢慢就与 UTC 不同步了。
闰秒时计算北京时间的标准方法为:
2015-07-01 07.59.572015-07-01 07.59.582015-07-01 07.59.592015-07-01 07.59.60 <-- 闰秒2015-07-01 08.00.002015-07-01 08.00.012015-07-01 08.00.02
2015-07-01 07:59:58.0002015-07-01 07:59:58.5002015-07-01 07:59:59.0002015-07-01 07:59:59.5002015-07-01 08:00:00.000 <-- 插入闰秒2015-07-01 07:59:59.0002015-07-01 07:59:59.5002015-07-01 08:00:00.0002015-07-01 08:00:00.500
IERS 确定闰秒后,一些时间传播服务还会发布闰秒通知。这包括德国长波发射机 DCF77 和卫星巡航系统 GPS 示例。因此,可解码从那些系统获取信号的接收器也可以解码闰秒通知。如果在所应用协议中包含闰秒信息(例如接收器传送的时间字符串),则从那些接收器读取时间的应用程序也可以确定闰秒通知。请注意,时间代码接收器只能将闰秒通知转发到应用程序,同时正确计时。正确处理闰秒是应用程序和(/或)操作系统的任务。
# 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的系统
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
2)运行PTP的系统
PTP(精确时间协议)中交换的时间戳通常采用不包含闰秒的TAI(国际原子时);但 ptp4l 和 phc2sys 将设置内核标签,插入闰秒以便系统时钟继续以 UTC 运行。然后该内核就可以正常插入闰秒。3)未运行NTP或者PTP的系统
例如:如果某个系统位于中国时区,您可以将其重新配置为通过运行以下命令报告闰秒修正时间,
例如在TS2系统中,tzdata包的版本为tzdata-2015a-1.tl2.noarch,执行完上述拷贝后,则会在闰秒发生时间2015年7月1日8点自动插入闰秒。cp /usr/share/zoneinfo/right/Asia/Shanghai /etc/localtime
4)windows系统
历史上,因为linux内核的一些问题,闰秒对系统造成多次影响。比如CPU利用率高会给生产环境带了不少挑战。2012年实施闰秒时,国外不少知名网站出现了临时服务中断。当2015年闰秒再度来临时,工程师们修复了部分2012年出现的问题,但却东窗事发——发现了新的问题。后续亦是如此。闰秒让互联网企业如鲠在喉。
1) linux-2.6.22以前内核版本的闰秒死锁
07年的commit: http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=746976a301ac9c9aa10d7d42454f8d6cdad8ff2b;hp=872aad45d6174570dd2e1defc3efee50f2cfcc722)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 ?? ()
该现象部分因为hrtick_start_fair()函数的引入。是由commit 8f4d37ec (high-res preemption tick)引发,这大概在2.6.25版本引入。但是在2.6.25之前的内核,不会发生这个死锁。
Sles11使用2.6.27内核,属于比较危险的部分内核。但是Novell声称已经引入了commit b845b517b5e3706a3729f6ea83b88ab85f0725b0,因而不存在该问题,而且几个小时的实验后系统仍然正常。
3)linux-3.4内核版本的系统活锁
08年的commit中为了解决之前遇到的leap second问题而将对leap second的处理从second_overflow()中独立出来,使用定时器来完成此工作。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);
08年的commit:https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=7dffa3c673fbcf835cd7be80bb4aec8ad3f51168 12年的commit: https://lkml.org/lkml/2012/3/15/616
4)linux-2.6.32内核插入闰秒可能出现高CPU消耗
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");
}
date -s "`date`"
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)取消闰秒的后续可能
也许解决这个问题的可能方法是让世界时和国际原子时之间的差异增加到一分钟,但专家估计调整时长在50到100年之间。而有提议指出,无需在时钟上增加闰分钟,而是将某一天的最后一分钟变为需要两分钟;也有人建议停止校正,同时公布世界时和国际原子时之间不断增长的时刻差。
腾讯工程师技术干货直达: