3

Linux 系统时间变更通知

 2 years ago
source link: https://blog.lilydjwg.me/2014/3/14/system-time-change-notification-on-linux.43577.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Linux 系统时间变更通知

本文来自依云's Blog,转载请注明。

每一次,系统从挂起状态恢复,系统日志里总会多这么几行:

systemd[1]: Time has been changed
crond[324]: time disparity of 698 minutes detected

一个来自 systemd,一个来自 dcron,都是说系统时间改变了。那么它们是怎么知道系统时间改变的呢?

dcron 的代码很少,所以很快就可以找到。因为 dcron 每一次的睡眠时长它自己知道,所以当它再次从睡眠状态醒来,发现时间变化特别大时,它就会察觉到。也就是说,小的变化它会察觉不到的。

systemd 呢?这家伙一直在使用 Linux 新加特性,比如上次发现的 prctl 的 PR_SET_CHILD_SUBREAPER 功能。这次它也没有让我失望,它使用了 timerfd 的一个鲜为人知的标志位——TFD_TIMER_CANCEL_ON_SET。timerfd 是 Linux 2.6.25 引入的特性,而TFD_TIMER_CANCEL_ON_SET这个标志位则据说 Linux 3.0 引入的,但是到目前为止(man-pages 3.61),手册里没有提到它,系统头文件里也没有它。

这个标志位是干什么的呢?其实很简单,是当系统时钟被重设时向程序发送通知,包括通过系统调用设置系统时间,以及系统从硬件时钟更新时间时。当事件发生时,在该 timerfd 上的读取操作会返回 -1 表示失败,而 errno 被设置成ECANCELED。下边是一个简单的演示程序,在系统时间变化时打印一条消息:

#include<unistd.h>
#include<sys/timerfd.h>
#include<stdbool.h>
#include<stdint.h>
#include<errno.h>
#include<stdlib.h>
#include<stdio.h>
#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
#ifndef TFD_TIMER_CANCEL_ON_SET
#  define TFD_TIMER_CANCEL_ON_SET (1 << 1)
#endif
int main(int argc, char **argv){
int fd;
struct itimerspec its = {
.it_value.tv_sec = TIME_T_MAX,
};
fd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC);
if(fd < 0){
perror("timerfd_create");
exit(1);
}
if(timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET,
&its, NULL) < 0) {
perror("timerfd_settime");
exit(1);
}
uint64_t exp;
ssize_t s;
while(true){
s = read(fd, &exp, sizeof(uint64_t));
if(s == -1 && errno == ECANCELED){
printf("time changed.\n");
}else{
printf("meow? s=%zd, exp=%lu\n", s, exp);
}
}
return 0;
}

编译并运行该程序,然后拿 date 命令设置时间试试吧 =w= 当然记得用虚拟机哦,因为系统时间乱掉的时候会发生不好的事情喵~

date 091508002012

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK