Timekeeping in VMware Virtual Machines を読み解く (2)

高精度タイマによる補正

前回はちょっと簡略して書きました。VMware にも関連のあることなので,ほとんど推測に基づくものですが,少し詳しく書きます。

Linux のシステム時刻(時計)において秒針*1を進めるのは,PIT による割り込みです。ただ,割り込みだけに取りこぼしが発生するかもしれませんし,間隔が正確とも限りません。また,割り込み間隔以上の精度の時刻を得ることもできません。ですから,利用可能であればより高精度のタイマを補正に用います。

時刻間隔の「補完」についてはおおむね予想がつくので省略し,「補正」について考えます。

たとえば PIT を1/100秒間隔に設定したとします(HZ=100)。このとき1秒間に100回の割り込みが発生します。しかしシステム(カーネル)が非常に忙しくて80回しか割り込み処理ルーチンを呼び出せないとすると,実時間1秒の間にシステム時計は0.8秒しかすすまないことになります(時刻の遅延)。

もし他にもう1個ストップウォッチがあったとしたらどうでしょう(高精度タイマの存在)。システム時間1秒後にその alternative なストップウォッチを見て,「PIT によると1秒しか進んでないけど実際には1.25秒すすんどるやんけ。lost ticks や。時計をちょっとすすめとかな。」とより正確な時刻にすることができます(時刻の補正)。

一般にはこの高精度タイマ(ACPI Timer や TSC 等)による補正は良い方向にはたらきます。ただ,kernel 2.6.17 以前ではこの補正がいきすぎてしまい,時刻が実時間より速くすすんでしまう場合もあるようです(時刻の過剰補正→【Clock in a Linux Guest Runs More Slowly or Quickly Than Real Time】参照)。

時刻の設定メカニズム

この項は原典とは関係ありませんが後々触れるかもしれないので,一応説明します。

Linux においてシステム時刻を設定するメカニズムには次の4つがあげられます。厳密にメカニズムというと後者2つだけですが。

  1. 恒常的な時刻調整
  2. adjtime(3) による漸次的(遅効的)な時刻設定
  3. (以上の基盤となる)adjtimex(2) による高度な時刻調整
  4. settimeofday(2) による過渡的(速攻的)な時刻設定

まず4についてですが,「今12:00にしてね」とシステムに伝えると,その時点でシステム時計を 12:00 にセットする,という一番単純な仕組みです。次に BSD 互換 API である2の adjtime(3) ですが,これは指定した時刻と現在の時刻がずれていた場合,ちょっとずつ補正していくというものです。たとえば,現在12:00のシステム時計を12:01に設定しようとした場合,実時間上1秒ごとに秒針を2秒ずつ進めると,実時間1分後には実時刻・システム時刻ともに12:02になります。逆に12:01のシステム時計を12:00に設定しようとした場合,実時間1秒ごとに秒針を0.5秒ずつすすめればよいことになります(この方式をとると,システム時計を「逆行」させる必要がありません)。NTP デーモンである ntpd ではこの4と2の両者を利用して無理なく時刻を合わせようとします(参考→【http://www.oracle.co.jp/2shin/ora66/18.html】)。

1の「恒常的な時刻調整」ですが,例えば1日に必ず5分遅れるとわかっている時計(割り込み)があれば,秒針の進め方を0.35%ほど早めておけば正確な時刻が刻める,という考えに基づくものです。VMware とはあまり関係ないので深入りしませんが,ntp 等のない環境でできるだけ時刻を正確に保つ方法としてはhttp://kinsan.main.jp/wiki/wiki.cgi?page=Linux%A4%C7%BB%FE%B7%D7%A4%CE%B9%E7%A4%EF%A4%BB%CA%FDが参考になります。

3の adjtimex(2) というものは(おそらく)1と2を担うための Linux 独自のシステムコールです。省略します。

以上の仕組みについては http://www.linux.or.jp/JF/JFdocs/Clock/ も参照してみてください。



なかなか本題に入れませんね……

*1:実際には秒以下の単位であり,tick と呼びます