假设我的程序在正确configuration的linux机器上运行以处理闰秒 。 如何configuration完成(NTP,configuration文件)不应该与这个问题有关。
实际上,内核会在本月的UTC结束时再插入一秒或跳过一秒钟。 这对gettimeofday(2)读取的时间值有影响。 UTC的最后一个UTC秒是重复的或跳过的。 这里列出了闰秒的时间读数示例。
我的问题:如何在C / C ++程序中找出在月底发生的闰秒,以及在哪个方向上。 那么如何在linux上实现以下function?
int leap_seconds_scheduled_for_end_of_month() { if (/*kernel_will_insert_extra_second?*/) return 1; if (/*kernel_will_skip_over_last_second?*/) return -1; return 0; }
如果月末远未来,结果不正确。 (对于我的目的来说远远大于2秒)答案在最后一秒之前的一个可能的闰秒(即UTC的最后一天的23:59:58)之前必须是正确的(*)。 如果我了解到闰秒发生之后是不够的,因为我必须提前做好准备。
PHP,crontab,DateInterval:将可读的时间间隔转换为crontab格式
Gameboy模拟器播放比预期的更快
用C ++超时input
无需安装额外的软件包即可获得毫秒级的时间?
我一直试图在sysfs或procfs中find任何闰秒指示符,但迄今为止一直不成功。
(*)当然,如果内核本身在发生之前仅仅知道闰秒的一小部分,例如由于整个上个月的NTP服务中断,那么答案就不可能是正确的。 没关系。
一个timepec在tv_nsec字段中可以有超过1秒的纳秒时间吗?
QueryPerformanceCounter的性能影响
在Linux内核空间测量亚微秒精度的时间
chroot-ed环境中的当前时间与当地时间不同
内核保留一个标志,该标志定义在当前(UTC)日结束时是否添加(或删除)闰秒。 在支持的地方,当闰秒还剩下不到一天的时候,它由ntpd设置。
因此,假设(a)你的ntp守护进程从它的源头获得它的注意,并且(b)你的ntp守护进程正确地确定你的内核支持这个标志,你基本上可以通过检查这个标志来得到一天的闰秒警告。
我不认为这个标志是通过sysfs或procfs暴露的; 但是通过adjtimex系统调用很容易从C中检索:
#include <sys/timex.h> #include <stdio.h> int main(int argc,char **argv) { struct timex buf; int res; buf.modes = 0; res = adjtimex(&buf); if(res < 0) { perror("Error calling adjtimex"); return 1; } printf("clock status: %in",res); return 0; }
结果是1或2,如果闰秒正在运行,由adjtimex(2)给出:
成功时,adjtimex()返回时钟状态:
#define TIME_OK 0 /* clock synchronized */ #define TIME_INS 1 /* insert leap second */ #define TIME_DEL 2 /* delete leap second */ #define TIME_OOP 3 /* leap second in progress */ #define TIME_WAIT 4 /* leap second has occurred */ #define TIME_BAD 5 /* clock not synchronized */
AFAIK,内核不会预先安排闰秒预告。 这些知识由NTP守护进程维护,当系统时钟发生变化时, ntpd发出adjtimex(2)系统调用来调整系统时钟。
捅你的ntpd守护进程:
$ /usr/sbin/ntpq -c 'lassoc' -c "mrv &1 &999 leap,srcadr,stratum" ind assid status conf reach auth condition last_event cnt =========================================================== 1 5159 80a3 yes no none reject unreachable 10 2 5160 968a yes yes none sys.peer sys_peer 8 srcadr=LOCAL(0),leap=00,stratum=10 srcadr=timeserver.example.com,stratum=4
当你看到“01”跳跃时,你将有一个飞跃。 对于你的ntp服务器配置的每个源码,“srcaddr”这一行将会重复,所以你可能有多个条目,那里(我的例子返回虚拟本地系统cl
因此,对于一个C ++解决方案,你必须做一个丑陋的/usr/sbin/ntpq fork / exec,并捕获输出,或者挖掘ntpq使用的套接字协议与NTP守护进程对话,自己来抓取守护进程的响应。
感谢Sam Varshavchik和richvdh,他们都为这个问题提供了非常有用的答案。 昨天晚上,闰秒已经发生了,现在我使用在几台计算机上运行的两种方法来读取示例。
为了便于阅读,我将数据发布在单独的答案中,而不是评论。 下面的C程序已经被用来读取时间,并确定是否会发生闰秒:
#include <stdio.h> #include <sys/time.h> #include <time.h> #include <string.h> #include <sys/timex.h> #include <unistd.h> /* This C function implements Sam Varshavchik's method */ int leap_seconds_scheduled_for_end_of_month() { char ntpq_output[1024]; /* The path to ntpq might have to be adjusted for your system */ FILE * ntpq_stream = popen("/usr/bin/ntpq -c lassoc -c 'mrv &1 &999 leap,stratum'","r"); memset(ntpq_output,1024); fread(ntpq_output,1,1022,ntpq_stream); pclose(ntpq_stream); /* This finds the first leap=xx occurrence in ntpq's output. If multiple upstream ntp servers are configured,there will be one line of text for each of them. It would be good to check that the stratum=xx value in the same line is not 16 (which means invalid). If it is,better use the leap=xx value from another output line. Not done here for simplicity. */ char * leap_bits = strstr(ntpq_output,"leap="); if (leap_bits == NULL) return 0; leap_bits += 5; if (leap_bits[0] == '0' && leap_bits[1] == '1') return 1; if (leap_bits[0] == '1' && leap_bits[1] == '0') return -1; return 0; } /* This function prints the following data in a single line of text: gettimeofday,clock_gettime for CLOCK_MONOTONIC,adjtimex (richvdh's solution),and Sam's solution */ print_current_data() { struct timeval tv = {0,0}; gettimeofday(&tv,NULL); printf("%u.%06u ",(unsigned)tv.tv_sec,(unsigned)tv.tv_usec); struct timespec ts = {0,0}; clock_gettime(CLOCK_MONOTONIC,&ts); printf("%u.%09u ",(unsigned)ts.tv_sec,(unsigned)ts.tv_nsec); struct timex buf; buf.modes = 0; printf("%d ",adjtimex(&buf)); printf("%2dn",leap_seconds_scheduled_for_end_of_month()); } /* main just calls print_current_data repeatedly. If we are far away from the leap second,it sleeps between printouts to reduce the ammount of data. */ int main(int argc,char ** argv) { /* this is the time_t value for the second after the 2015 leap second */ time_t post_leap = 1435708800; for(;;) { time_t Now = time(NULL); print_current_data(); if (Now < (post_leap-60)) { sleep(30); } else if (Now < (post_leap - 10)) { sleep(5); } else if (Now < (post_leap - 2)) { usleep(500000); } else if (Now <= (post_leap + 1)) { } else if (Now > (post_leap + 120)) { return 0; } else if (Now > (post_leap + 1)) { usleep(500000); } } return 0; }
这里是昨晚的数据。 为了不淹没你的数据,我只在前后插入一行有趣的改变发生,并指出[…跳过到xxx]的省略。
每个数据行包含以下字段:
gettimeofday,clock_gettime(CLOCK_MONOTONIC),adjtimex(richvdh的解决方案)和Sam的解决方案(调用ntpq和parse输出)。
系统1:使用内核3.0.62运行Linaro 13.04的Cubieboard
ntp包是版本1:4.2.6.p3 + dfsg-1ubuntu5
$ ./print_leap 1435703927.419452 7971483.087902293 1 1 [... skipping to the second before the leap second] 1435708798.992813 7976354.661146951 1 1 1435708799.014143 7976354.682476687 1 1 [... skipping to the leap second] 1435708799.982292 7976355.650624344 1 1 1435708799.007776 7976355.676111361 3 1 [... skipping to the second after the leap second] 1435708799.985337 7976356.653668890 3 1 1435708800.007414 7976356.675746846 4 1 [... skipping to a change in the adjtimex reading] 1435708844.685062 7976401.353401529 4 1 1435708845.204844 7976401.873191115 0 1 [... skipping to the last programmatic reading] 1435708921.124692 7976477.793033504 0 1
在闰秒后约4.5小时手动检查,萨姆的方法也回到0。
系统2:运行Debian 8的Cubieboard 2,内核为3.4.107-cubietruck
ntp包是版本1:4.2.6.p5 + dfsg-7
$ ./print_leap 1435703847.373559 948687.514617591 1 1 [... skipping to the second before the leap second] 1435708798.998980 953639.139771365 1 1 1435708799.028375 953639.169165960 1 1 [... skipping to the leap second] 1435708799.986449 953640.127238940 1 1 1435708799.020017 953640.160809364 3 1 [... skipping to the second after the leap second] 1435708799.984912 953641.125702281 3 1 1435708800.012875 953641.153666113 4 1 [... skipping to the last programmatic reading] 1435708921.268660 953762.409472677 4 1 $ ./print_leap # manual reading several hours later 1435725362.080253 970203.221195342 0 0
系统3:使用内核3.12.35 +运行Raspbian 7的RaspBerrypi模型B.
ntp包是版本1:4.2.6.p5 + dfsg-2 + deb7u1
$ ./print_leap 1435704085.032222 313606.064883453 1 1 [... skipping to the second before the leap second] 1435708798.969855 318320.002146274 1 1 1435708799.040573 318320.072865002 1 1 [... skipping to the leap second] 1435708799.996887 318321.029180271 1 1 1435708799.071282 318321.103573880 3 1 [... skipping to the second after the leap second] 1435708799.945609 318321.977898784 3 1 1435708800.017116 318322.049407486 4 1 [... skipping to the last programmatic reading] 1435708921.136882 318443.169189210 4 1 $ ./print_leap # manual reading several hours later 1435732962.263988 342484.296617478 0 0
系统4:运行Ubuntu 15.04的Intel NUC DN2820,内核为3.19.0-20-generic
ntp包是版本1:4.2.6.p5 + dfsg-3ubuntu6
注意闰秒开始时的小故障:
nuc@nuc1:~$ ./print_leap 1435703986.534020 1305198.740735208 1 1 [... skipping to the second before the leap second] 1435708798.991478 1310011.198081950 1 1 1435708799.000905 1310011.207509156 1 1 [... skipping to the leap second] 1435708799.989309 1310012.195913702 1 1 1435708800.000738 1310012.207343097 1 1 1435708799.016079 1310012.222683886 3 1 [... skipping to the second after the leap second] 1435708799.999616 1310013.206220581 3 1 1435708800.012446 1310013.219050861 4 1 [... skipping to the last programmatic reading] 1435708921.047985 1310134.254602197 4 1 nuc@nuc1:~$ ./print_leap # manual reading several hours later 1435725807.234545 1327020.441295352 0 0
系统5:运行Ubuntu 14.04的Intel NUC DN2820,内核3.13.0-53-generic
ntp包是版本1:4.2.6.p5 + dfsg-3ubuntu2.14.04.3
nuc@nuc2:~$ ./print_leap 1435704031.137881 323125.995674014 1 1 [... skipping to the second before the leap second] 1435708798.995646 327893.853350053 1 1 1435708799.007936 327893.865640804 1 1 [... skipping to the leap second] 1435708799.995589 327894.853293338 1 1 1435708799.007527 327894.865231774 3 1 [... skipping to the second after the leap second] 1435708799.998111 327895.855815619 3 1 1435708800.013461 327895.871165134 4 1 [... skipping to the last programmatic reading] 1435708921.282149 328017.139858311 4 1 nuc@nuc2:~$ ./print_leap # manual reading several hours later 1435725557.303859 344653.161689304 0 0
系统6:运行Ubuntu 14.04的联想E330笔记本电脑,内核为3.13.0-55-generic
ntp包是版本1:4.2.6.p5 + dfsg-3ubuntu2.14.04.3
注意闰秒结束后的不一致。
$ ./print_leap 1435706912.426035 82874.419021628 1 1 [... skipping to the second before the leap second] 1435708798.929115 84760.922063075 1 1 1435708799.013439 84761.006388727 1 1 [... skipping to the leap second] 1435708799.981243 84761.974190967 1 1 1435708799.049374 84762.042323977 3 1 [... skipping to the second after the leap second] 1435708799.913464 84762.906413341 3 1 1435708800.000183 84762.993132942 3 1 1435708800.477323 84763.470272391 4 1 [... skipping to a change in the ntpq reading] 1435708840.094271 84803.087225581 4 1 1435708840.618538 84803.611493081 4 0 [... skipping to the last programmatic reading] 1435708921.042020 84884.034975833 4 0 $ ./print_leap # manual reading several hours later 1435724912.944500 100875.937487693 0 0
系统7:运行Ubuntu 14.04,内核为3.13.0-53-generic的AMD Phenom cpu的戴尔服务器
ntp包是版本1:4.2.6.p5 + dfsg-3ubuntu2.14.04.3
注意闰秒开始和结束时的故障
$ ./print_leap 1435704125.933343 2210517.393979537 1 1 [... skipping to the second before the leap second] 1435708798.998012 2215190.458598770 1 1 1435708799.002893 2215190.463480413 1 1 [... skipping to the leap second] 1435708799.994690 2215191.455273816 1 1 1435708800.001917 2215191.462501431 1 1 1435708799.013505 2215191.474092236 3 1 [... skipping to the second after the leap second] 1435708799.992212 2215192.452796670 3 1 1435708800.000210 2215192.460794341 3 1 1435708800.006819 2215192.467403224 4 1 [... skipping to the last programmatic reading] 1435708921.323808 2215313.784400730 4 1 $ ./print_leap # manual reading several hours later 1435726838.319240 2233230.779876616 0 0
系统8:运行Debian 6的虚拟服务器,使用主机的内核2.6.32-028stab094.3
客人系统中没有安装ntp软件包。 NTP可能运行在主机上,也可能不运行。 主机为访客系统提供时间。
$ ./print_leap sh: /usr/bin/ntpq: No such file or directory 1435704206.353115 114299811.979591258 1 0 [... skipping to the second before the leap second] sh: /usr/bin/ntpq: No such file or directory 1435708798.994303 114304404.620727643 1 0 sh: /usr/bin/ntpq: No such file or directory 1435708799.000291 114304404.626715249 1 0 [... skipping to the leap second] sh: /usr/bin/ntpq: No such file or directory 1435708799.999445 114304405.625868168 1 0 sh: /usr/bin/ntpq: No such file or directory 1435708799.004273 114304405.630696209 3 0 [... skipping to the second after the leap second] sh: /usr/bin/ntpq: No such file or directory 1435708799.998234 114304406.624656721 3 0 sh: /usr/bin/ntpq: No such file or directory 1435708800.003723 114304406.630146580 4 0 [... skipping to the last programmatic reading] sh: /usr/bin/ntpq: No such file or directory 1435708921.185876 114304527.812316105 4 0 $ ./print_leap # manual reading several hours later sh: /usr/bin/ntpq: No such file or directory 1435727243.250130 114322849.876607481 0 0
系统9:运行Debian 8的虚拟服务器,使用主机的内核2.6.32-37-pve
客人系统中没有安装ntp软件包。 NTP可能运行在主机上,也可能不运行。 主机为访客系统提供时间。
$ ./print_leap sh: 1: /usr/bin/ntpq: not found 1435704263.521402 627240.363389922 1 0 [... skipping to the second before the leap second] sh: 1: /usr/bin/ntpq: not found 1435708798.991150 631775.833122648 1 0 sh: 1: /usr/bin/ntpq: not found 1435708799.062267 631775.904240598 1 0 [... skipping to the leap second] sh: 1: /usr/bin/ntpq: not found 1435708799.986215 631776.828187732 1 0 sh: 1: /usr/bin/ntpq: not found 1435708799.061678 631776.903650529 3 0 [... skipping to the second after the leap second] sh: 1: /usr/bin/ntpq: not found 1435708799.987007 631777.828979335 3 0 sh: 1: /usr/bin/ntpq: not found 1435708800.061094 631777.903067236 4 0 [... skipping to the last programmatic reading] sh: 1: /usr/bin/ntpq: not found 1435708921.380148 631899.222124765 4 0 $ ./print_leap # manual reading several hours later sh: 1: /usr/bin/ntpq: not found 1435727152.545742 650130.387735253 0 0
NTP公共池问题
NTP池中的一些服务器没有公布闰秒 。 在运气有点不好的情况下,如果池中的所有上游服务器都无法公布闰秒,则永远不会知道。 因此,如果本地应用程序需要闰秒警告,则必须通知您的本地NTP守护程序使用配置文件的闰秒。
毛刺/不一致
当顺序查询当前时间和当前闰秒状态时,由于两次函数调用之间经过的时间很明显,值可能不一致。
然而,观察到调用之间所经过的时间无法解释的其他不一致之处。 两台计算机(系统4和7)已经开始了闰秒,由gettimeofday返回1435708800的时间值,然后在下一个读数中转回到1435708799。 两台计算机,系统6和7,在闰秒后开始秒,其中adjtimex值仍然表示我们在闰秒之内。
结论
这两种建议的方法都可以达到这个目的:预先知道在正确配置的Linux系统上是否会出现闰秒。
这种先进知识使应用程序能够确定CLOCK_REALTIME和CLOCK_MONOTONIC之间的差异,然后在闰秒附近使用CLOCK_MONOTONIC来确定时间。
如果没有做这种准备,没有任何方法可以帮助确定闰秒附近的时间。 但这不是我所要求的。
这两种方法都需要额外的知识,即闰秒只会在UTC月份结束时进行精确计划。 从上个月的NTP闰秒警告似乎在下一个UTC月份的前几分钟被淹没。 因此,只有在UTC月末在不久的将来才能解释。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。