IAマシンのもつ各種計時ハードウェア

IAマシンは、歴史的事情などにより、数多くの計時ハードウェアを持っています。以下、順に説明します。
現代の CPU ではいずれも CPU に内蔵されているか、PCH (Platform Control Hub) など CPU 近辺のチップに内蔵されており、少数の水晶発振器を分周/逓倍して利用していると考えられます。したがって、精度 (細かさ) やアクセス速度、機能は異なりますが安定度は同等と考えられます。

 

  1. PIT (i8254)
  2. Local APIC タイマー
  3. TSC
  4. ACPI 電源管理タイマー
  5. HPET
  6. PV Clock

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 にはあった