Linux のシステム時計の時刻あわせ



2004年 7月 3日

松本徳真 < matsu@netfort.gr.jp >


このファイルは、もともとは LMSでの資料として作成した物で、 単独では、いまいち役に立たないので、少し文章を追加してみました。 追加した説明はこの色です。

概要

常時接続に限らず、ネットワークに接続されたコンピュータが勝手な時を 刻んでいたのでは、色々な問題が発生します。
本セミナーでは、Linux のシステム時計を個人ユーザーが可能な範囲でま ともにする事を考えます。

1) UNIX time, 協定世界時について、glibc での実装に付いて簡単に解説。
2) ntp 等有名な、時刻合わせ、時刻比較プログラムに付いて簡単な紹介。
3) 現在企画中の簡易時刻修正プログラムの紹介。



はじめに

私は、この春から 上の写真の町に住んでいます。そのような訳で、時刻とかその辺に ちょっとうるさい人になる事にしました(嘘)。



協定世界時(UTC)について

協定世界時, 閏秒等の単語は聞いた事があると思いますが、これらが 何物か把握している人は少ないようです。コンピュータのシステム時計 に付いて考える前に協定世界時に付いて、簡単に解説してみたいと思います。

時を計る物差しは、この世に無数に存在します。皆がそれぞれ勝手な時計を 使っていると、混乱してしまいます。食事の待ち合わせ時間の指定で、 「俺様の腹のすいた時」と指定されても困ってしまいます。
そこで、皆で使う共通の時刻システムとして決められたのが、 協定世界時(UTC)です。


日本標準時

協定世界時で 06:00:00 頃と言えば日本ではお昼過ぎです。日常生活の便利の ためには、12:00:00 が、お昼であって欲しいものです。そこで、世界各地で 地方時と言うものが運用されています。東西に長い国では、複数の地方時が ある場合もあります。日本では、日本標準時(JST)が使用されています。 日本標準時は協定世界時に対してちょうど 9時間進んでいます。協定世界時で 2004年6月10日 0時0分0秒は、日本標準時では 2004年6月10日 9時0分0秒に なります。協定世界時と地方時の変換は、何時間進んでいるか遅れているか がわかれば機械的に簡単に計算できます(但し、夏時間が存在する、非文明国は 除く)。



ここの説明で、地方時と言う単語を間違って書いていますが、標準時 としておけば良かったかな。 本来の地方時は、経度が異なれば異なります。東京の 地方時と大阪の地方時は別物なのです。ただ、密接に繋がりあった地域内 で、時刻系が異なるのは不便です。そこで、日本国内では、東経135度に おける地方時を標準時として使用する事が法律で決められています。これが、 日本標準時です(聞いた話では、法律の条文中には日本標準時という単語 はなく、中央標準時となっているそうな)。
世界各国で標準時があり、それらの多くは、 UTC に対して整数時間の オフセット(ずれ)があるように 0, 15, 30, 45, ... 度のように15度の 倍数の経度における地方時を標準時として採用しています。もちろん 例外はあって、例えば America/St_Johns では、-3.5時間となっています。
UNIX で local time という単語が出て来ますが、これは、地方時では なく、各地方での標準時程度にとらえて下さい。

真太陽時



原始的な時計の一つに日時計があります。地面に棒を立てて、その影の位置で 時刻を知る事が出来ると言う簡単な物です。 <余談>上の写真は、第二神明道路の明石SA で撮影した物です。この日時計は、多少近代的です。棒が斜めに立っていますが、 実は、この棒の傾きは、設置場所の緯度と等しく、棒の先端は真北を向いています。 つまり、この棒は、地球の自転軸と並行になるよう設置されているのです。</余談>
このように、日時計で計る時刻は、地球表面から見た見掛け上の太陽の動きを、 直接的に表現する物で、日出、日没に同期して生活する人間にとっては、 非常に便利な時刻系と言えます。

恒星時, 平均太陽時

恒星時 恒星の観測から得られる。ある恒星が子午線を通過してから、 次に子午線を通過するまでの時間を一日とする。地球の自転をを 直接時刻に反映する。
真太陽時 地球表面から見た太陽の動きを基準にした時刻系。地球の軌道が楕円形である、 地軸が約23.4度傾いている等の事情で、季節によって一日の長さが異なる。
平均
太陽時
太陽時の季節変動分を平均化したもの。恒星時から計算で得ることができる。



±1時間程度の精度で良ければ、日時計は優れた時計です。この時刻系は、 地球の自転と、太陽のまわりを回る公転の組合せになります。そこで、 地球の自転軸は、公転面と垂直でない、地球の公転軌道は、楕円形で、 太陽に近い時は速く、遠い時は遅く運動すると言う影響で、季節によって、 一日の長さが異なる(昼の長さが異なるという話とは別の問題です)という 事になります。
日時計は、夜間や、曇の日は使用できません。その間は、何らかの時計に よって補間する訳ですが、一日の長さ(ひいては一秒の長さ)が変動する 時刻を表現するのは大変な話です。
そこで、季節変動分を平均化して作られたのが、平均太陽時です。 平均太陽時は、恒星の地球表面から観測したデータを元に、作成します。 恒星は、ほぼ無限遠のかなたにあるので、これは、地球の自転を直接的に 測定する事を意味します(実際には、無限遠のかなたにある訳ではなく、 視差がありますし、地球の公転による光行差とか恒星自身の固有運動の影響 とかありますが、これらは観測データの処理時に取り除きます)。
観測で得られた恒星時に公転による影響を計算で加えれば、平均太陽時が 得られます。

国際原子時(TAI)

まず 1秒の長さを定義して、一定の速度で時が進んで行く時刻システム
技術の進歩とともにますます精密な時刻システムになって行く。
国際原子時は、世界各地の原子時計を比較しながら運営されている。

133Cs 原子の基底状態の2つの超微細準位の間の遷移に対応する 放射の 9 192 631 770 周期の継続時間。

セミナーではここに、原子時計の写真を貼っていました。
原子時計や、周波数標準器等の写真を見たい方は、

http://jjy.nict.go.jp/
独立行政法人 情報通信研究機構 日本標準時グループ

http://www2.nict.go.jp/dk/c251/index.html
独立行政法人 情報通信研究機構 原子周波数標準グループ

をさまよって下さい。



協定世界時(UTC)



太陽時を基本とする世界時(UT)は、人間の日常生活に便利な時刻系であるが、 世界時がどんどん精密化されて行くと、地球の自転の不規則な運動に、 悩まされてきました。そこで、1972年から開始された現行の協定世界時(UTC)は、 国際原子時(TAI)を基準とすることになりました。協定世界時(UTC)は、 国際原子時(TAI)と同期し、世界時(UT1)からのずれが大きくなると、 閏秒(leap second)の挿入削除により調整されます。

TAI, UT1, UTC, leapsecond

国際原子時(TAI), 協定世界時(UTC), 世界時(UT1) がどのように 時を刻んで切るか模式的に示したのが以下の図です。


ここで、気を付けたいのは、TAI と UTC は、常に整数秒のオフセット がある事です。1972年1月1日 00:00:00 UTC の時点でちょうど 10秒、 2004年8月現在 ちょうど 32秒ずれています。
UTC と UT1 を比べると UTC が、随分と進んでいます。これを調整する ために 23:59:60 という閏秒が挿入され、翌日の 00:00:00 の時点では、 今度は UTCが遅れた状態になりました。

数ヵ月経過

閏秒の調整により、UTCが、UT1に対し進んだ状態だったのが、長い時間が 経つとUTC と UT1 が並びました。
さらに数ヵ月経過

そして、再び、UTCが少し進んだ状態になって来ました。
さらに、さらに数ヵ月経過

更に時間が経過すると、UTCがかなり進んだ状態になって来ました。 ここで、再び閏秒(赤線)を挿入して、UTCが遅れた状態にしました。 このような操作を繰り返し、UTC が UT1 に対して±0.9秒以内に なるよう調整しながら運用して行きます。



UNIX での実装

time_t time( time_t *time);
int gettimeofday( struct timeval *tv, struct timezone *tz );
等のシステムコールで、ある起点(1970/01/01 00:00:00 GMT) からの 経過時間を秒、またはマイクロ秒単位で得ることができる。
localtime(), localtime_r(),
gmtime(), gmtime_r()

等の libc に含まれる関数を使って、年、月、日、時、分、秒に 変換することができる。



古代の UNIX では、

unix time と呼ばれる time() の戻り値を、 年、月、日、時、分、秒に変換する際、1日を 86400( = 24時間*60分*60秒)秒 と決め打ちしていたが、かといって、TAI でも、UT1, UT2 とも 言えない不思議な時刻システムであった。
昔は、正確な時刻を取得する安価な手段がなかったので、そんなに 気にすることもなかったということか。
ただ、現在では、高速なネットワークで多くのコンピュータが接続される 中で、時刻同期の重要性は高まっているが、あいまいな基準では困るよね?



glibc, FreeBSD では、

glibc 2.1 以降では、従来の unix time 的な使用法ができる一方、 設定により、UTC に対応することもできる。 FreeBSDの libc でも、かなり早い時期から対応している。 他のUNIXシステムはわかりません。必要な人は各自で調べてね。







UTC対応の考え方

time() 関数の戻り値を、1970/1/1 00:00:10 TAI からの 経過秒数と定義する。
あるいは、1972/1/1 00:00:00 UTC の値を 63072000( =2*365*24*60*60 ) と 定義する。
time() 関数の戻り値から、年月日時分秒に変換するには、 閏秒(leapsecond)実施 のデータが必要。タイムゾーンが Asia/Tokyo に設定されている場合、
/usr/share/zoneinfo/Asia/Tokyo
に閏秒実施データが含まれていれば、UTC 対応のシステムとなり、含まれていな ければ、従来同様のシステムになる。



UTC対応システムの問題点

不定期に実施される閏秒の実施が決まれば、新しい閏秒実施データを取得し、
/usr/share/zoneinfo
以下を再構築する必要がある。
入手先
ftp://elsie.nci.nih.gov/pub/
自動化するのはそう難しくないので大した問題ではない。

UTC に対応していないアプリケーションが結構ありそう。



UTC 未対応アプリケーション例

cron は、もともと指定時間は分までしか指定できないので、 気にしないというのもひとつの手。あるいは cronexec を使え。
http://www.netfort.gr.jp/~tosihisa/cronexec/

http://www.netfort.gr.jp/~tosihisa/cronexec/

時刻合わせの雄, ntp が、まじめに対応しない。DJB作 clockspeed を使うか?でも Pentium または互換CPU (TSCレジスタを持つもの) に限られる。





ちょっと休憩

なにか質問ありますか?











時刻合わせの実際

時刻合わせの要素
時刻 時刻をあわせる。
周波数 時計のすすみ具合を調整する。


毎日時報を聞きながら腕時計をあわせる
→ ”時刻”をあわせる。

いつも遅れがちな時計を、時計屋さんで直してもらった
→ ”周波数”をあわせる



時計を合わせるとい行為において一般には、「時刻」合わせの方に のみ気を取られがちですが、「周波数」を合わせる事も重要です。 ただ、クオーツ時計が普及してから、「周波数」をユーザが調整できる 時計が、ほぼ皆無になったこともあり、ついつい忘れられがちです。

基準時刻の取得方法

GPS
長波JJY
テレフォンJJY
ネットワーク経由

日本国内での使用を考えると、基準時刻の取得方法として使えそうな ものはこの程度でしょう。真面目な時刻合わせを考えなければ、 TV放送、ラジオ放送の時報、NTTの117サービス等色々あります。
このセミナーでは、この中から、ネットワーク経由の時刻の取得について 簡単に解説しました。まだ地域差があるとは言え、比較的安価にインターネット への常時接続が可能になってきた現在では、多くの人に取って一番 気軽に使用できる方法と考えたからです。









ネットワーク経由の遅延対策


NTP プロトコルなどでは、比較的遅延の大きいネットワーク回線を利用して、 高精度の時刻比較が出来るよう、上記図のような方法を使用しています。
T0 .. クライアントが問い合わせのパケットを送出した時刻。
T1 .. サーバが問い合わせのパケットを受け取った時刻。
T2 .. サーバが返答のパケットを送出した時刻。
T3 .. クライアントが返答のパケットを受け取った時刻
を測定します。ここで、T0, T3 は、狂っているかも知れないクライアントの 時計で測定。T1,T2 は、正しいと考えられるサーバ側の時計で計測 しています。これを上記図の式の様に計算すると、クライアントの時計が どの程度ずれているかを知る事が出来ます。
ここでは、前提として、
1. 上り、下りのパケットは、同じ遅延時間(delay)を要する。
2. クライアントの時計のズレ量(offset)は、パケットが往復する間に 大きく変化しない。

現在一般家庭に普及している ADSL のように、上り下りの帯域が非対称な 回線では、1. の前提を期待するのは無理ですね。ADSL回線経由で、 ntpd を利用して時計を合わせた場合、見掛けの offset で 0.001 秒以内 程度は十分いけると思いますが、遅延(delay) が 50 m sec あるなら、 0.03 秒以内程度には合ってるかなぁ〜程度に考えておくべきでしょう。



ネットワーク経由の基準時刻取得プロトコル

ntp 123/udp ネットワーク遅延対策あり
サーバ: ntpd
クライアント: ntpd, ntpdate, clockspeed, その他いっぱい
daytime
13/udp,tcp
time
37/udp,tcp
ネットワーク遅延対策なし
サーバ: inetd 内蔵
クライアント: netdate
ICMP TIMESTAMP ネットワーク遅延対策あり
サーバ: TCP/IP プロトコルスタック
クライアント: clockdiff

上記表のようにネットワーク経由で、時刻比較を行うプロトコルは たくさんあります。通常の公開サーバでは、ntp プロトコルを使用 しています。ICMP TIMESTAMP については、TCP/IP の実装方法にも よりますが、カーネルの TCP/IP プロトコルスタック自体がその 機能を持っていますので、ntpd の様な daemon を意識して起動しなくても ネットワークに接続するだけで機能してしまいます( iptables 等で、 切る事は当然可能) 。ping で反応を返すようなマシンは、かなりの 確率でこの機能が有効です。とはいえ、マシンの管理者が、意識して 公開しているので無い以上勝手に使用して良いとは思えません。 実験してみたい人は、自分の管理するマシンか、管理者に了解を とってお試し下さい。



裏技その1

#!/bin/sh
host=$1
datestr=`wget -S --spider $host 2>&1\
|grep " Date: "` || exit
echo "${datestr##* Date: }"

http で、サーバーに問い合わせると、ヘッダー内にに時刻情報が 含まれています。上記スクリプトでは、ヘッダーのみの取得を していますので、アクセスされた側から見れば、ページが更新されたかどうか の確認の為のアクセスに見えるでしょう。
まあこれも目的外利用ですので、自分の管理するマシンか、管理者に了解を とってお試し下さい。







裏技その2

#!/bin/sh
tmstr=`wget -q -O - \
http://www2.crl.go.jp/cgi-bin/JST.cgi \
|grep Standard`
tmstr=${tmstr#*\"}
tmstr=${tmstr%\"*}
echo $tmstr

これは、国内の周波数標準を管理したり、日本標準時を報知したりする部署 をもつ
独立行政法人 情報通信研究機構 のサイト内にある、簡易的に、 Webブラウザ内に日本標準時を表示する JavaScript を利用した物です。 同様のスクリプトは、各種メーリングリストでも見掛けましたし、 これを利用して時刻合わせを行う Windows 用のソフトウェアも存在 します。





裏技は使うな!!

http://www2.crl.go.jp/cgi-bin/JST.cgi
をブラウザで表示すると、

「日本標準時表示のページ」は URL が変更になりました。
トップページからたどり直してください。

と表示されます。

この cgi を利用して時刻合わせをするプログラム が配布されたため、想定外のアクセスがあり、URL 変更を余儀なくされ たそうです。
にくいことに、このページのコメントに、時刻データが含まれていて、 例の時刻合わせプログラムが動作し続けるように工夫がされています。
組織によっては DDoS 攻撃だ、不正アクセスだと騒ぎだしかねないこと を考えると、なかなか大人の対応だと思います。

移行前のページでは、ネットワーク遅延+α程度の精度が望めましたが、 新しいページでは、1秒程度の誤差が見込まれます。昔のページでは、 正秒になるまで、ページの送信を一時停止すると言う技で、少しでも精度を 上げようと努力していたようですが、この工夫が、時刻合わせプログラムによる 大量アクセスに対して耐えられなかった原因ではないかと推測します。







公開 NTP サーバ

ntp1.jst.mfeed.ad.jp
ntp2.jst.mfeed.ad.jp
ntp3.jst.mfeed.ad.jp

ntp プロトコルで時刻合わせを行うことが可能なら、これらの サーバを使うと良いでしょう。現在は、試験公開中ですが、 本運用になるまで、停止されることはなさそうです。

http://www.jst.mfeed.ad.jp/ を参照して下さい。







ntpd

ntpd パッケージに含まれる ntpdate を cron 等で定期的に実行すると、 時刻合わせができる。
ntpd を実行すれば、周波数同期も行う。
ntpd は、ntp サーバとしても機能する。アクセス制御が 出来るとは言え、用途によってはちょっと不安。
周波数同期は ソフトウェア PLL によるかなり高度なもの。
時刻同期のみと、周波数同期も行った場合の時刻のずれの模式図。


上は、ntpdate を定期的に実行して時刻を合わせた場合、下は、 ntpd を使用して、システム時計と、サーバ側の時刻を PLL で 同期を取った場合の模式図。


PLL の概念







clockspeed

時刻と、周波数の調整を行う。
ntpd がソフトウェア PLL によって、適宜周波数を調整するのに対して、 長時間の時刻ずれの測定により、周波数を決定する。
始めから UTC に対応した設計がなされている。
頻繁に基準時刻を取得できない環境で有利になる。
t = p * TSC +offset
で、p と offset を決定し、適宜システム時刻に反映する。
settimeofday(), ではなく、adjtime() を使用しているので、 時刻が逆戻りしたりする事はない。
Pentium および 互換 CPU の持つ TSC レジスタに依存。



現在企画(実験)中

Linux には、移植性に難はあるが、adjtimex() というシステムコールが 存在する(たぶん ntpd 由来)。
clockspeed のように、長時間の時刻ずれの測定結果から、システム時計 の周波数を決定し、adjtimex() で、システム時計の周波数を変更する。
ntpd, clockspeed の様な daemon でなく、時々ユーザが、手動、cron 経由、 あるいは、ネットワーク接続スクリプトから実行する。



以下は省略。近日中に、
ここ で公開予定。












おしまい

質問をどうぞ。