時刻設定に関する覚え書き
家のNTPサーバの構成を整理していたら、色々忘れているなぁと感じたので、
ここらでNTPについて頭の中を整理しておくかと思ったら、既に1年前に書いてた。
記憶力の無さに目まいがしたが、前段階的な知識を整理したので、メモしておきます。
ハードウェアクロックとシステムクロック
まずハードウェア時計とシステム時計について整理します。
どちらの値も精度にムラがあるため、次第にズレていきます。
用語 | 意味 |
---|---|
ハードウェアクロック | ハードウェア(BIOS)上の時刻。電源OFF状態でも維持されます。 |
リアルタイムクロック | ハードウェアクロックと同じ |
システムクロック | OS上の時刻(ソフトウェア時計) |
Linux上では以下のコマンドで確認が可能です。
# hwclock 2016年11月05日 16時23分47秒 -0.223672 秒 # hwclock --utc 2016年11月05日 16時23分51秒 -0.692678 秒 # hwclock --localtime 2016年11月05日 07時23分56秒 -0.161490 秒 # date 2016年 11月 5日 土曜日 16:24:12 JST # date -u 2016年 11月 5日 土曜日 07:24:15 UTC
また、CentOS7からはtimedatectlでも表示できます。
# timedatectl Local time: 土 2016-11-05 16:24:20 JST Universal time: 土 2016-11-05 07:24:20 UTC RTC time: 土 2016-11-05 07:24:20 Time zone: Asia/Tokyo (JST, +0900) NTP enabled: yes NTP synchronized: yes RTC in local TZ: no DST active: n/a
タイムゾーンとローカルタイム
次にタイムゾーンとローカルタイムです。
UTCはGMTは原則同義なのですが、GMTは調整されない点が違うようです。
UTC | 協定世界時(Coordinated Universal Time) |
GMT | グジニッジ標準時。UTCに対して1000年で18秒ずれる |
JST | 日本標準時(Japan Standard Time)UTC+9時間 |
Localtime | そのエリアでの標準時(日本の場合JST) |
(余談)hwclockのローカルタイムが9時間ズレている点
先ほどのhwclock --localtimeと、timedatectlのRTC timeがJSTより9時間前になっています。
なぜBIOS上の時間がJSTの時間とずれているのかと言うと、ESXi上の仮想マシンだからです。
ESXiはタイムゾーンが指定できず、UTCで動作するため仮想HWもUTCになります。
このため、ハードウェアクロックをUTCとして扱っています。
余談ですが、vSphere Clientで接続するとイベント情報がローカルタイムで表示されますが、
これはクライアント側のタイムゾーンを読み取って、変換して表示しています。
ハードウェアクロックとシステムクロックの同期の仕組み
ハードウェアクロックとシステムクロックを同期する仕組みはバージョンによって様々です。
基本的には、起動時にハードウェアクロックをシステムクロックに読み込み、
停止時にシステムクロックをハードウェアクロックに読み込む処理を行います。
CentOS5の場合
CentOS5では起動時に/etc/rc.d/rc.sysinit内でhwclock --hctosysを呼び出しています。
/etc/sysconfig/clockからタイムゾーンやUTC設定の情報を読み取り、
パラメータに合わせてhwclockコマンドを実行します。
(CentOS5.11の場合、274行目以降で記載されています。)
# Set the system clock. update_boot_stage RCclock ARC=0 SRM=0 UTC=0 if [ -f /etc/sysconfig/clock ]; then . /etc/sysconfig/clock # convert old style clock config to new values if [ "${CLOCKMODE}" = "GMT" ]; then UTC=true elif [ "${CLOCKMODE}" = "ARC" ]; then ARC=true fi fi CLOCKDEF="" CLOCKFLAGS="$CLOCKFLAGS --hctosys" case "$UTC" in yes|true) CLOCKFLAGS="$CLOCKFLAGS --utc" CLOCKDEF="$CLOCKDEF (utc)" ;; no|false) CLOCKFLAGS="$CLOCKFLAGS --localtime" CLOCKDEF="$CLOCKDEF (localtime)" ;; esac case "$ARC" in yes|true) CLOCKFLAGS="$CLOCKFLAGS --arc" CLOCKDEF="$CLOCKDEF (arc)" ;; esac case "$SRM" in yes|true) CLOCKFLAGS="$CLOCKFLAGS --srm" CLOCKDEF="$CLOCKDEF (srm)" ;; esac [ -x /sbin/hwclock ] && /sbin/hwclock $CLOCKFLAGS
停止時の処理については基本的には同じで、/etc/rc.d/init.d/haltに記載されてます。
同様に/etc/sysconfig/clockからタイムゾーンやUTC設定の情報を読み取り、
パラメータに合わせてhwclockコマンドを実行します。
(CentOS5.11の場合、89行目以降で記載されています。)
# Sync the system clock. ARC=0 SRM=0 UTC=0 if [ -f /etc/sysconfig/clock ]; then . /etc/sysconfig/clock # convert old style clock config to new values if [ "${CLOCKMODE}" = "GMT" ]; then UTC=true elif [ "${CLOCKMODE}" = "ARC" ]; then ARC=true fi fi CLOCKDEF="" CLOCKFLAGS="$CLOCKFLAGS --systohc" case "$UTC" in yes|true) CLOCKFLAGS="$CLOCKFLAGS -u"; CLOCKDEF="$CLOCKDEF (utc)"; ;; no|false) CLOCKFLAGS="$CLOCKFLAGS --localtime"; CLOCKDEF="$CLOCKDEF (localtime)"; ;; esac case "$ARC" in yes|true) CLOCKFLAGS="$CLOCKFLAGS -A"; CLOCKDEF="$CLOCKDEF (arc)"; ;; esac case "$SRM" in yes|true) CLOCKFLAGS="$CLOCKFLAGS -S"; CLOCKDEF="$CLOCKDEF (srm)"; ;; esac [ -x /sbin/hwclock -a -e /dev/rtc -a -e /sys/class/misc/rtc/dev ] && action $"Syncing hardware clock to system time" /sbin/hwclock $CLOCKFLAGS
CentOS6の場合
CentOS6の場合、上記のスクリプトは変更されています。
まず起動時にはhwclockコマンドは実行されません。
じゃあ、どこでやっているのかというとkernel内で読み取られているようす。
dmesgコマンドを確認するとこんな感じで表示されます。
rtc_cmos 00:04: setting system clock to 2016-11-05 08:43:47 UTC (1478335427)
また、終了時は/etc/rc.d/init.d/haltに記載されていますが、
記載内容が異なっています。
(CentOS6.8の場合、107行目に記載されています。)
[ -x /sbin/hwclock -a -e /dev/rtc ] && action $"Syncing hardware clock to system time" /sbin/hwclock --systohc
CentOS7の場合
さらにCentOS7でも変更は続きます。
公式サイトでも言われていますが、RHEL7以降ではhwclockは起動、停止時に実行されません。
とはいうものの、起動時にkernel側でハードウェアクロックの読み込みは行われています。
[ 0.558357] rtc_cmos 00:03: setting system clock to 2016-10-30 06:08:22 UTC (1477807702)
しかし、停止時にhwclockは見当たりません。cronでもなくカーネルで設定されているようです。
どうやらカーネルのconfigにおいて、CONFIG_GENERIC_CMOS_UPDATE=yになっていると、
kernel内で11分おきに同期(ソフトウェアクロックをRTCに書き込む)が走るようです。
「とあるSIerの憂鬱」のこちらの記事が詳しいです。
確かにKernel3.10の/kernel/time/ntp.cには以下のような記述がありました。
488 if (abs(now.tv_nsec - (NSEC_PER_SEC / 2)) <= tick_nsec / 2) { 489 struct timespec adjust = now; 490 491 fail = -ENODEV; 492 if (persistent_clock_is_local) 493 adjust.tv_sec -= (sys_tz.tz_minuteswest * 60); 494 #ifdef CONFIG_GENERIC_CMOS_UPDATE 495 fail = update_persistent_clock(adjust); 496 #endif 497 #ifdef CONFIG_RTC_SYSTOHC 498 if (fail == -ENODEV) 499 fail = rtc_set_ntp_time(adjust); 500 #endif 501 }