Linux
systemd
ntp
timedatectl

systemd環境におけるハードウェアクロック調整

切迫した事情があったわけではありませんが、気になって調べたので結果をまとめておきます。
テスト環境はopenSUSE Tumbleweedですが、まぁsystemd環境では大概同じでしょう。

まずはsystemdに尋ねる

$ timedatectl statusして、得られる出力はこんな感じになります。

bash
$ timedatectl status
      Local time: 火 2017-11-07 18:29:15 JST
  Universal time: 火 2017-11-07 09:29:15 UTC
        RTC time: 火 2017-11-07 18:29:15
       Time zone: Asia/Tokyo (JST, +0900)
 Network time on: no
NTP synchronized: yes
 RTC in local TZ: no
$ 

ただし上例は一点を除いて全く正常で、その点も例外事項で異常ではありません。
で、以下の箇所を確認しましょう。

  1. NTPを使う設定になっているのにNTP synchronized: noになっていないか
  2. 特別な理由がないのにRTC in local TZ: yesになっていないか

NTPが機能するようにする

お使いのディストリビューションでどのNTPデーモンを使っているかに注意して、使用中のNTPデーモンの設定を確認しましょう。
systemd-timesyncd(8)、ntpd(1)、chrony(1)、openntpd1など色々な実装がありますが、Network time on: yesとなるのはsystemd-timesyncdがenabledな場合に限られます2ので多くの環境では当該項目はあてにならないことを頭に入れておいてください。先ほどの例では、ntpd(1)により時刻が同期されていたためnoながらNTP synchronized: yesになっています。
ここでは各実装における設定ファイルの書き方には触れません。

RTCがローカル時刻に設定されている場合

Windows環境を上書きしてインストールした環境などでよくなっていますが、Linuxにおいてこの状態は好ましくありません3。Windowsとのデュアルブート仕様である4などといった特別な事情がなければ、コマンド一発# timedatectl set-local-rtc 0で直してしまいましょう。

なお、この操作により/etc/adjtime5が書き換えられるはずですが、手元の環境では真っ白のままでした。

ハードウェア時刻のお世話をする

まずは$ cat /etc/adjtimeでadjtimeファイルの中身を見てみましょう。

/etc/adjtime
0.000000 1510044982 0.000000
1510044982
UTC

まず、3行目はタイムゾーンではなく「UTCかローカルタイムか」が書かれている部分です。ローカルタイムの場合、LOCALと書かれますが先ほど書いた通りそれは推奨されませんし、timedatectl(8)で変更した場合ここが書き換えられるので、手で触れることはないでしょう。

で、2行目と1行目の真ん中にあるのがdrift値で、ここに整数が入っていれば一回以上このファイルは書き込まれているわけです。
1行目の両端にある数字は、おそらく2回以上drift調整が行われた時に入る差分の値または係数かと思います6

1行目、2行目がオール0だった場合、これまたコマンド一発# hwclock --utc --update-drift --systohcで書くことができます。
しかし、システムクロックとハードウェアクロックの差分で調整するという仕組み上どうしても一回書くだけではあまり意味をなしてくれないでしょう。何らかの事情でNTPが機能しなくなった場合には当然役に立ちますから、週一や月一でsystemd.timer(5)なりcron(8)なりに入れておいて損はないでしょう7

なお、ntpdを運用中の場合はどうもntpd自身がよろしく調整してくれるらしいという情報も(但し手元の環境ではそんなことありませんでした)。その場合は下手に触らない方がいいと思います。

調整の完了

単に$ timedatectl status$ cat /etc/adjtimeの出力を確認して、おかしなところがなければOKです。
hwclockコマンドをtimer作ったりcronに入れたりした人は、drift値の変動が収まったタイミングを見計らって# systemctl disableなりcrontabから削除なりしても良いでしょう。

お疲れ様でした。


  1. ある事情のためPiで使ってるので知っていますが、ハリセンボン族の一員であるopenntpdにはopenntpdとしてのmanpageはありません。その代わり、具体的なコマンドについてのntpd(8)ntpctl(8)がありますが、他を快調にカッコ付けした続きにこうくるとかなり難しいものがあります。。一般的な方のntpdと丸かぶり。 

  2. 20181020追記:この項目の名前ですが、現在のsystemdではsystemd-timesyncd.service activeというものに変更されています。普通のNTPデーモン使ってる人には微妙な変更ですよね。time-sync.targetあたりで上手いことできないのでしょうか。 

  3. その理由はArchWikiに書かれており、要約すると「これをやると夏時間の類による切り替えをLinux側ではハンドルできず、それだけのためにローカル時刻RTCの実装された別のOSを起動する必要が発生する」というものであるようです。 

  4. Archwikiによれば、Windows側をUTCなハードウェアクロックに適応させる方法もあるようです。 

  5. 2015年の更新で現れたFHS3.0において、このパスは/var/lib/hwclock/adjtimeというものに定義の変更がされています。しかし2018年10月現在util-linux側ではこの変更に追従していないため、現在もほとんどの環境では/etc/adjtimeとなっているはずです。 

  6. このファイル、ググった限りでは正確な定義がどこにも載っていないのです。hwclock(8)のソースを読めば多分わかるのでしょうがそこまではやっておらず。。→20180904追記:読みました。私の稚拙なCスキルで読んだ限りですが、どうも右端の数字は「RTCを書けないが読めはするプログラムがズレを認識した時にここに書いておくと、hwclock(8)はこれを加味してズレを計測する」といったような役割があるようです。つまりRTCに触れる存在がhwclock(8)のみであればここの値は誰も書かないので0こそ正常値だということのようです。 

  7. systemd.timer(5)を使った場合はサービスファイルも書かねばなりませんが、ジャーナルに出力をリダイレクトさせることができるので--verbose付きのサービスファイルを書いておけば時刻調整の様子を克明に知ることができるようになります。