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

検証は,おもに,

CPU
CeleronD 341 (2.93 GHz; Single core, EM64T capable, HT non-capable, VT-x non-capable)
Chipset
Intel 3000
Host OS
CentOS 5.0 x86_64 (Linux 2.6.18-8.1.8)
VMS
Virtual Server 1.0.3
Guest OS
CentOS 5.0 i386 (Linux 2.6.18-8.1.8)

で行います/した。

つまり,Linux 2.6.18 以降についてしか扱わないってことです。

PC/AT における「時刻」と「時間」のソース

原典と【http://tiki.is.os-omicron.org/tiki.cgi?c=v&p=Linux%2F%A5%BF%A5%A4%A5%DE%B4%C9%CD%FD】をもとに分類すると,

  • 「時刻」のソース
    • RTC
  • 「時間」のソース
    • RTC
    • PIT (Programmable Interval Timer)
    • Local APIC (Advanced Programmable Interrupt Controller) (a.k.a. lapic)
    • ACPI Timer
    • TSC* (Time Stamp Counter)
    • HPET (High Precision Event Timer)

TSCi686 以降でサポートされているカウンタで,セット・リセットできない(読み出し専用)し,割り込み(アラーム)機能もありません(それ以外の上述のものはすべて割り込み機能を持っている)。CPU クロックベースなので省電力機能等でクロックが変動した場合に周波数が変動する可能性がありますし,TSC を読み出す命令である RDTSC 命令ですらアウトオブオーダー実行の影響を受けえます。などなどの欠点はありますが,Linux 2.6 のデフォルト動作では TSC が使えるときには使います(後述)。

HPET は ICH4 あたりから実装された比較的新しいタイマデバイスですが VMware ではサポートしていないのでパスします。

APIC と ACPI と Windows の関係については【http://www.daw-pc.info/hard/acpi/acpiapicmain.htm】参照。少なくとも「時間」に関する APIC は,I/O APIC ではなく,各 CPU の内包する Local APIC についてです。

Linux における「時刻」

Linux にはハードウェア時刻(RTC)とシステム時刻の2つがあります。

Linux におけるシステム時刻情報の保持ですが,

  1. ブート時に RTC からハードウェア時刻を読み出しシステム時刻とする
  2. その後「一定時間ごと」にシステム時刻を刻む
  3. 場合によっては,適宜高精度タイマにより補正をシステム時刻に加える
  4. システムコール(gettimeofday)により時刻を取得する際,システム時刻を返す
  5. シャットダウン時にシステム時刻をハードウェア時刻(RTC)に書き戻す

2 の「一定時間ごと」というものには基本的に PIT を用います。

Linux におけると書きましたが,*BSD においても同様です。また Windows も同様です*1

なぜ常に RTC を使わないかというと,おそらく RTC が CMOS 駆動のため信頼に足るデバイスではない(とかつて考えられていた)ということが一点と,どうせ「一定時間ごと」の割り込みがマルチタスクシステムのスケジューラに必要なので,時刻が必要になる度にコストのかかる RTC へのアクセスを行うより,その割り込みを流用すればいいじゃん,ということだと思います。

3 の「高精度タイマによる補正」ですが,Linux 2.6 ではデフォルトで利用可能であればACPI Timer を用います(ソース・時点未確認)。少なくともRedHat 系では TSC を用います。補正に関する動作は kernel の clocksource パラメータにより指定できます。

clocksource=pit
PIT のみ利用・補正なし? (kernel/arch/i386/i8253.c)
clocksource=tsc
TSC を利用 (kernel/arch/i386/tsc.c)
clocksource=hpet
HPET を利用 (kernel/arch/i386/hpet.c)
clocksource=acpi_pm
ACPI Timer を利用 (drivers/clocksource/acpi_pm.c)
clocksource=cyclone
Cyclone Timer を利用*2(driver/clocksource/cyclone.c)
clocksource=scx200_hrt
Geode SCx200 用 (drivers/clocksource/scx200_hrt.c)

2 の「一定時間ごと」ですが,今日日の Linux 2.6 では 1000 Hz です。カーネルコンパイル時の HZ というパラメータ*3により指定します。

*1:Windows ではハードウェア環境によっては PIT の代わりに RTC Timer を用います

*2:cyclone の詳細は不明です;IBM のサーバ向けチップセットに実装されているんですかね?

*3:これが sysctl 等でいじれるパラメータだったら平和だったんですが