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
- 「時間」のソース
TSC は i686 以降でサポートされているカウンタで,セット・リセットできない(読み出し専用)し,割り込み(アラーム)機能もありません(それ以外の上述のものはすべて割り込み機能を持っている)。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 におけるシステム時刻情報の保持ですが,
- ブート時に RTC からハードウェア時刻を読み出しシステム時刻とする
- その後「一定時間ごと」にシステム時刻を刻む
- 場合によっては,適宜高精度タイマにより補正をシステム時刻に加える
- システムコール(gettimeofday)により時刻を取得する際,システム時刻を返す
- シャットダウン時にシステム時刻をハードウェア時刻(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により指定します。