IAマシンのもつ各種計時ハードウェア
IAマシンは、歴史的事情などにより、数多くの計時ハードウェアを持っています。以下、順に説明します。
現代の CPU ではいずれも CPU に内蔵されているか、PCH (Platform Control Hub) など CPU 近辺のチップに内蔵されており、少数の水晶発振器を分周/逓倍して利用していると考えられます。したがって、精度 (細かさ) やアクセス速度、機能は異なりますが安定度は同等と考えられます。
1. PIT (i8254)
PIT は Programmable Interrupt Timer の略で、最初の IBM PC 以来搭載されて続けています。
1.19318MHz のクロックを数える 16bit のダウンカウンターで、周期的な割り込みをかけるモードが利用されてきました (ワンショットモードもありますが、あまり正確ではないとされています)。割り込み先は、IRQ 0 に固定されています。
アクセスは遅いですが、クロックが一定であり、かつ全てのマシンに必ず搭載されているため、フォールバック用および他のタイマーのキャリブレーション用として利用されています。
参考までに、「Linux / x86_64の割り込み処理 第1回」より割り込みコントローラとの関係を示す図を引用しておきます。
2. Local APIC タイマー
APIC は Advanced Programmable Interrupt Controller の略で、PCアーキテクチャがマルチCPU に対応した際に導入されました。
周辺デバイスの割り込み信号を受ける I/O APIC と、CPU と 1:1 に対応する Local APIC (LAPIC) からなるアーキテクチャで、論理的には、デバイスからの割り込みを受けた I/O APIC が、設定に応じて、APICバスを通じ適切な Local APIC (つまりCPU) の適切な割り込みベクタに割り込む、という形です (詳細は、「 Linux / x86_64の割り込み処理 第1回 割り込みコントローラ」をご参照ください)。概ね、Pentium 4 世代以降であれば、Uniprocessor モデルであっても APICアーキテクチャを採用しています。
Local APICタイマー (LAPICタイマー) は、その名の通り LAPIC の機能の一つとして実装されたもので、バスクロックを数える 32bit のタイマーです。対応する CPU の任意のベクター番号に割り込みをかけることができますが、他の CPU からアクセスできず、また他の CPU に割り込みをかけることができません。
現代の CPU では LAPIC が CPU に内蔵されているため、CPU が省電力モード (深い Cステート) に入ると停止するモデルがあります。
3. TSC
TSC は Time Stamp Counter の略です。各論理 CPU の持つレジスターで、クロック (例: 2.66GHz) を数える 64bit のカウンターです。
高速アクセスが特徴である一方、クロックが変動する (PowerNow、SpeedStep) と、TSC の速度も変動するモデルや、省電力モードで停止するモデルがあります。これらのモデルでは、計時のためには補正が必要となりますが、現代の CPU (Pentium 4 世代の途中から) では、常に一定クロックで進むようになりました。なお、一定値に達すると割り込みをかける、などの機能はありません。
4. ACPI 電源管理タイマー
3.579545MHz を入力とする 24bit または 32bit のタイマーになります。本来はシステムのアイドル時間を調べるためのタイマーですが、仕様上汎用的に使用することができます。ACPI 電源管理タイマーにも割り込みをかける機能はありません。
5. HPET
HPET は High-Precision Event Timer の略で、10MHz 以上のクロックをカウントする 32bit または 64bit のカウンターです。
PITを代替する目的で導入されました。32 までのマッチレジスター (タイマーと呼び、0から順に番号が振られる) を持ち、カウンター値がマッチレジスター値に達すると割り込みをかけることができます (ワンショットのほか、周期的な割り込みもかけられる。この場合割り込むたびにマッチレジスターが自動的に更新される)。
I/O APIC 経由のほか、PCI の MSI (Message Signaled Interrupt: メッセージシグナル割り込み ※「 Linux / x86_64の割り込み処理 第2回」参照) のように、特定の CPU に直接割り込みベクターを送信できるものもあります (FSB 割り込み)。仕様上 8つまで (タイマー数でいうと 256 まで) 持てますが、カーネル内では 1 つのみが利用されます。
タイマー 0 とタイマー 1 は、互換モードを備え、それぞれ PIT と同じ IRQ 0、RTC と同じ IRQ 8 に接続されます。また、/dev/hpet 経由でユーザーランドにもタイマー機能が提供されます。この目的には 2つ目以降の HPET もサポートされます。
6. PV Clock
Linux KVM や Xen などの仮想化ゲストでは、他のゲストやホストの処理の都合で、タイマー割り込みが遅れたり、失われたりすることがあります。結果的にゲストの時刻が狂う原因となります。
Linux KVM や Xen では、ホストの時計と連動した Para-virtualized (PV) Clock の機構が用意されており、高精度かつ正確 (ホストの時計が NTP や GPS などにより校正されている前提で) な時計がゲストに提供されます。
仕組みは以下の通りです。
- あらかじめホストとゲストで共有されるメモリを用意しておく
- 仮想マシンに入る (VMEnter) 直前に、ホストは共有メモリに以下を書き込む
– 現在の TSC (tsc_timestamp)
– ホスト起動からの時間 (system_timestamp、単位ナノ秒)
– TSC とナノ秒を変換する係数 (tsc_to_system_mul、tsc_shift) - ゲストは、これらの情報から以下の計算で起動からの時間を得る
system_timestamp +
(((rdtsc() – tsc_timestamp) * tsc_to_system_mul) >> tsc_shift)
別に、システム起動時刻も得られるようになっており、足し算することで現在時刻が得られます。system_timestamp は、ホストの時計と連動しておりますので、ホストの時計が正確であれば、ゲストの時計も正確なものとなります。
省電力機能とタイマー
現代の CPU では、省電力は熱設計の都合などで、CPU のクロックがかなり大きく変動したり、止まったりするのが普通です。CPUクロックを数えるような計時ハードウェアは、その影響を受けることがあります。これまでにも軽く触れましたが、以下にまとめます。
TSC
- もともとは、CPU クロックを数えるもの
- CPU クロックの変動により、進む速度が変わるモデルがある。インテルのメインストリーム系 CPU では Pentium 4 世代の途中まで
- Linux ではカーネル (cpufreq サブシステム) がクロックを制御するため、そのタイミングで実時間との変換のための係数を変更
- cpuid 命令で得られるプロセッサ情報のうち、Invariant TSC bit が 1 なら常に一定クロック (定格/最大クロックとは限らない) で進む。Linux では /proc/cpuinfo の flags: constant_tsc に反映
Local APIC タイマー
- バスクロックを数える
- 省電力モード時に止まるモデルがある
- Linuxでは、カーネル (cpuidle) がどの省電力モード (C ステート) に遷移するかを選択するので、そのタイミングで代替タイマーを設定
- cpuid 命令で得られるプロセッサ情報のうち、ARAT (APIC-Timer-always-running) bit が 1 なら省電力モードに限らず常に動作する。Linux では /proc/cpuinfo の flags: arat に反映
– 手元の Nahalem にはなく、SandyBridge にはあった