読者です 読者をやめる 読者になる 読者になる

雑木林

頭の中の整理と忘れないための確認メモ

時刻設定に関する覚え書き

家のNTPサーバの構成を整理していたら、色々忘れているなぁと感じたので、
ここらでNTPについて頭の中を整理しておくかと思ったら、既に1年前に書いてた。


zokibayashi.hatenablog.com

記憶力の無さに目まいがしたが、前段階的な知識を整理したので、メモしておきます。

ハードウェアクロックとシステムクロック

まずハードウェア時計とシステム時計について整理します。
どちらの値も精度にムラがあるため、次第にズレていきます。

用語 意味
ハードウェアクロック ハードウェア(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

 
 

タイムゾーンとローカルタイム

次にタイムゾーンとローカルタイムです。
UTCGMTは原則同義なのですが、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         }