2017/08/31

仮想マシンとハードウェアクロック

PCの中には時間を刻むハードウェアが内蔵されており、今が何月何日の、何時何分何秒かを把握しております。歴史的な事情からこのハードウェアは単一ではなく、複数のものが存在していたりします。かつては RTC (Real Time Clock)という仕組みだけでしたが、これがあまり精度が良くなかった、使い勝手が悪かったなどの理由で HPET が登場し、CPU には TSC というタイマーが用意されたのでこれを使って時刻を図ることが行われ、また ACPI には ACPI PMT というこれまた時間を計る機能が存在します。

ともあれ、OS は起動時にPCハードウェアのこれらの時計から時間を取得、カウンターを利用して、取得後どれぐらい経過したかを知り、OS自身で時間を管理しております。
例えば Linux の場合は、OSの終了時に hwclock コマンドを使ってPCのハードウェア側にOSが把握している時間をかき込みます。Windows などの場合もおそらくOS終了時に時刻を書き戻しているでしょう。この理由は、PCのハードウェア上の時刻はずれていることがままあるためです。

この PC上のハードウェアの時計は「どの時間帯」の時間を記録しているでしょうか?実はこれは決まっていません。ほとんどの場合、ハードウェアの時計はローカルタイム、現地時間を記録してます。一方、ハードウェアの時計が UTC(世界標準時) であることを前提としているシステムもあります。例えば ESXi がそうです。ESXi は物理ホスト上のハードウェアの時計に UTC での時間をかき込み、起動時も UTC であることを前提に読み込みをします。

一方、Windows や Linux では、OSの内部では UTC をベースに構成されていますが、時刻の表示時には指定されたタイムゾーンを加味して現地の時間を表示しております。ハードウェアの時計に対しても、ローカルタイムがかき込まれているものと考えるのがデフォルトとなってます。先の hwclock コマンドも、OSから読み出した時間を /etc/localtime ファイルの示す時間帯を考慮した現地時間でかき込みます。


● 仮想マシンの時間

VMware の場合、初回起動時には ホストOSの時間がそのまま使われます。VMware Player や Workstation, Fusion では、新しい仮想マシンを作成して起動、BIOS設定画面に入ると現地時間での時刻が表示されます。
かつての ESX でもそうでした。ESX の場合、COS(サービスコンソール) の Linux 上の /etc/localtime を参照して仮想マシンの仮想的なハードウェアの時計に「ローカル時間」をかき込んでました。
ESXi の場合、/etc/localtime のようなタイムゾーンを把握する枠組みがありません。vSphereClient などで時間を見ると現地時間が表示されますが、これらはクライアントソフトウェア側で変換して表示しているもので、ESXi との時間のやりとりは UTC をベースとしています。

このため、ESXi で仮想マシンを作成、初回の起動時にBIOS時間を見ると、UTCで表示されます。日本の場合、9時間前の時刻が表示されていることになります。

新規に作成した仮想マシン
17:42 頃のものだが、8:42となっており、初回起動時は9時間ずれてるのが分かる
二度目以降の起動では少々事情が異なります。ゲストOSが終了時に(仮想の)ハードウェアの時計に時間を設定した時、VMware はそのかき込まれた時間と自身が把握している時間を比較、「差分」を検知します。ESXi の場合、上のOSが「日本時間」をかき込んでくると、9時間早い時間がかき込まれたことを検知します。この差分は nvram ファイルに保存されます。

二度目以降の起動時は、自身の時刻にこの差分を加味した時刻を、仮想マシンのハードウェアの時計としてセットします。先の例の場合、UTCに9時間を加えた時刻をハードウェアの時計とします。これは日本時間になります。ゲストOSはこの時間を読み込みますので、結果として正しい時間になります。
この挙動は VMware のこのドキュメントの「Virtual CMOS RTC」に記載されています。


● 他のハイパーバイザの仮想マシン

Hyper-V の場合は Windows の時刻管理に準じるためあまり深いことを考える必要がありません。

今回、この記事を書く動機になったのは KVMについてのハードウェアの時計がどうなってるかを調べたことからでした。KVM では、仮想マシンのモニタである qemu-kvm コマンドの引数 -rtc にどう設定するかによって決まります。主に以下2つのオプションが使われます。

  • clock : 時刻を何処で管理するか
    • clock=vm : 仮想マシン側で独自に時刻を管理する
    • clock=host : ホストの時間をそのまま利用する
  • base : 仮想マシンの起動時、(仮想の)ハードウェアの時計を何処に設定するか
    • base=2010-12-03T01:02:00 : 2010/12/03 01:02:00 に設定する
    • base=utc : UTCでの現在時間に設定する
    • base=local : ホストOSのタイムゾーンを加味した現地時間に設定する

OpenSUSE のドキュメントの例をみると
qemu-kvm [...] -rtc clock=vm,base=2010-12-03T01:02:00
となってますが、これは「時刻は仮想マシンで独自に管理」「2010 12/03 1:02:00 をハードウェアの時計の開始時間」として設定することを意味します。

(AHVについては記述が誤っていたため一旦削除しました。あらためて正しい情報を書きます。)