• カーネルの行う時間管理
    • 現在日時の保持
      • ユーザプログラムからtime(), ftime(), gettimeofday()で取得可能。
    • タイマの仕組みの管理
      • 特定の時間が経過したことを通知

6.1 クロック回路とタイマ回路

  • クロック回路
    • 現在日時の取得や正確な時間計測に利用。
  • タイマ回路
    • 予め定義しておいた固定の周期で割り込みを発生。

6.1.1 リアルタイムクロック(RTC)

  • RTC(Real Time Clock)
  • ボタン型電池によりPCの電源とは無関係に動作。
  • モトローラ146818または同等品に実装。
  • 2Hz〜8196Hzの周波数でIRQ8割り込みを発生させる。
  • Linuxでは、日時を取得するためだけにRTCを使用。

6.1.2 タイムスタンプカウンタ(TSC)

  • TSC(Time Step Counter)
  • 80x86はCLK入力ピンで外部オシレータのクロック信号を受信。Pentium以降は64bitのTSCレジスタで取得可能。
  • 動作クロックによりTSCの加算周期が変わるが、同じカーネルイメージを使用可能。

6.1.3 プログラマブルインターバルタイマ(PIT)

  • PIT(Programmable Interval Timer)
  • カーネルが設定した特定の周期でタイマ割り込みを発生し続ける。
  • 割り込みの周期をtickと呼ぶ。
  • tickを短くするほどタイマの精度は上がるが、カーネルモードで動作する時間が長くなるというトレードオフがある。
  • タイマ割り込みマクロ
    • HZ:タイマ割り込み周波数(IBM PC互換機では1,000)
    • CLOCK_TICK_RATE:8254チップのオシレータ周波数(1,193,182)
    • LATCH:CLOCK_TICK_RATEとHZの比で、PITの設定に使用。

6.1.4 CPUローカルタイマ

  • CPUローカルタイマ(CPU local timer)
  • ローカルAPICで実装され、PITと同様に1回もしくは定期的な割り込みを発生するが違いもある。
    • PITのタイマカウンタは16bitであるが、APICのタイマカウンタは32bit。そのため、CPUローカルタイマはとても低い頻度で割り込みを発生させるように設定が可能。
    • PITはグローバルな割り込みを発生させるが、CPUローカルタイマは対象プロセッサのみに割り込みを送る。
    • CPUローカルタイマはバスクロック信号を基に設定するが、PITは独自にクロック信号を持っているためより柔軟に設定が可能。

6.1.5 高精度イベントタイマ(HPET)

  • HPET(High Precision Event Timer)
  • 最大8個の32bitまたは64bitカウンタを持つ。
  • クロック信号は10MHz以上で、カウンタは少なくとも100ナノ秒ごとに増加する。
  • 比較器がカウンタ値とマッチレジスタ値を比較し、ハードウェア割り込みを発生させる。
  • (一般にはまだ普及していないが)PITを置き換えるために設計されている。

6.1.6 ACPI電源管理タイマ(ACPI PMT)

  • ACPI PMT(ACPI Power Management Timer)
  • 周波数は約3.58MHz
  • バッテリ節約の目的でCPUの動作クロックを変更する場合は、TSCよりもACPI PMTが便利だが、HPETが利用できる場合は高機能なHPETの方を優先すべき。

6.2 時間管理の仕組み

  • 時間管理の基本的な使い分け(ローカルAPICタイマを持っていないマルチプロセッサシステムなどもある)
    • シングルプロセッサシステムでは、PITまたはHPETのグローバルタイマのどちらかの割り込みをきっかけに時間管理の処理を起動する。
    • マルチプロセッサシステムでは、グローバルタイマの割り込みをきっかけに全体的な処理を行い、ローカルAPICタイマ割り込みをきっかけにCPU每の処を起動する。

6.2.1 時間管理のデータ構造

  • timer_optsデータ構造体
    • mark_offsetメソッド:タイマ割り込みが発生した正確な時刻を適切なデータ構造体に記録する。
    • get_offsetメソッド:最後のタイマ割り込みからの経過時間(マイクロ秒)を計算する。
  • mark_offsetとget_offsetによりtickよりも正確な現在時刻を求める。(時刻補間 time interpolation)

高精度順のタイマオブジェクト
  1. timer_hpet:HPET
  2. timer_tsc:ACPI PMT
  3. timer_pit:PIT
  4. timer_nome:汎用のダミータイマ発生源

6.2.1.2 jiffies変数
  • システムが起動してからのtick数をカウント
  • 80x86系32bitでは約50日で循環するが、マクロによって正しい値が取得できるようになっている。
  • 32ビットアーキテクチャでは64ビットの値をアトミックに扱えないため、tickが1ミリ秒であれば数億年循環しないjiffies_64の下位32bitを参照している。

6.2.1.3 xtime変数
  • xtime変数
    • tv_sec:1970/01/01 00:00:00(UTC)からの経過秒数を保持
    • tv_nsec:現在秒からのナノ秒単位での経過時間を保持
  • tick每に更新。精度はtickによる?

6.2.2 単一プロセッサシステムにおける時間管理の仕組み

  • シングルプロセッサシステムでは、IRQ0のPIT割り込みをきっかけに実行。

6.2.2.1 時間管理の仕組みの初期化
  • xtime変数の初期化や使用可能なクロック回路・タイマ回路を判定して設定。(詳細は略)

6.2.2.2 タイマ割り込みハンドラ
  • jiffies_64の増加などを行う。(統計情報の算出などは後の章で説明。その他の詳細は略。)

6.2.3 マルチプロセッサシステムにおける時間管理の仕組み

  • PITまたはHPHEとローカルCPUタイマを利用する。

6.2.3.1 時間管理の仕組みの初期化
  • APIC初期化など。

6.2.3.2 グローバルタイマ割り込みハンドラ
  • シングルプロセッサ用といくつか違いがある。

6.2.3.3 ローカルタイマ割り込みハンドラ
  • カーネルコードのプロファイル処理とそのCPU上で実行しているカレントプロセスの実行時間確認などの特定のCPUに関連する時間管理処理を実行。

6.3 時刻と日付の更新

  • update_times()関数でxtime変数を更新。
  • wall_jiffies変数にxtimeを最後に更新した時刻を格納しておくことで、tick毎にxtime変数を更新しなくてもいいようになっている。最終的にはxtimeは正確なシステム時刻になる。

6.4 統計情報の更新

  • 実行プロセスのCPU資源制限の確認
  • ローカルCPUの作業負荷の更新(ロードアベレージ)
  • システムの平均負荷の算出
  • カーネルコードのプロファイリング

6.4.1 ローカルCPUの統計情報の更新

  1. ユーザモードであればaccount_userr_time()関数を呼び出し、カーネルモードであればaccount_system_time()を呼び出す。
    1. カレントディスクリプタのutime/stimeメンバを更新。
    2. CPU時間の合計が制限値に達していれば、SIGXCPUまたはSIGKILLシグナルを送る。
    3. プロセスのタイマを確認する。
    4. kstat上のカーネル統計情報を更新する。
  2. ローカルCPUにTIMER_SOFTIRQの起動を要求する。
  3. RCUで保護されている古くなったデータを回収する必要があれば、rcu_taskletの起動を要求する。
  4. カレントプロセスがクォンタムを使い切ったかどうか確認する。

6.4.2 システム負荷の管理

  • ロードアベレージを算出。TASK_RUNNINGかTASK_UNINTERRUPTIBLEのプロセス数をカウントする。
    • 0:実行中のプロセスは存在しない。
    • 1:1つのプロセスがCPUを使用している。
    • 1以上:複数の実行状態のプロセスがCPUを共有している。

6.4.3 カーネルコードのプロファイリング

  • readprofileと呼ばれるコードプロファイラでカーネルのホットスポットを監視。
  • モンテカルロアルゴリズムで実装。カーネルモードで実行中にタイマ割り込みがあったら、eipの値で実行中の処理を識別。
  • 有効化するにはブートパラメータを設定する。

6.4.4 NMIウォッチドッグの確認

6.5 ソフトウェアタイマと遅延関数

  • タイマがタイムアウトはjiffiesを比較することにより行う。
  • タイマ関数はタイムアウトしてから最大数百ミリ秒の遅延で実行されるため、リアルタイムアプリケーションには向いていない。
  • 動的タイマ:カーネルが使用するタイマ。
  • インターバルタイマ:ユーザモードで使用するタイマ。
  • 遅延関数:指定された時間のあいだ、小さい命令の繰り返しを行う。

6.5.1 動的タイマ

  • コールバック関数、タイマの期限をシステムが起動してからのtick数で指定。
  • リンクリストで各タイマを管理している。
6.5.1.1 動的タイマと競合状態
  • 削除可能な資源を処理する動的タイマなどは、タイマを止めてから資源の開放を行う。ただし、マルチプロセッサシステムでは、del_timer_sync()関数によって別のCPUでタイマ関数が実行されていないかを確認する。
  • 稼動状態のタイマのタイマ期限を変更する際などにもスピンロックで競合状態を避けるといった必要がある。
6.5.1.2 動的タイマのデータ構造
  • expireの値によりtick単位にいくつかのブロックに分けてタイマを管理している。0-255, 2^14-1, 2^20-1, 2^16-1, 2^32-1以内にタイムアウトする動的タイマの配列を管理。
6.5.1.3 動的タイマ処理
  • Linux2.6では効率化のため、タイマ割り込みハンドラではなく遅延処理で実装している。
  • (実装詳細は略)

6.5.2 動的タイマの適用例:nanosleep()システムコール

  1. カレントプロセスをTASK_INTERRUPTIBLEに変更。
  2. タイムアウトのtickを設定して他のプロセスを実行。
  3. 起床したらタイマを削除。

6.5.3 遅延関数

  • I/O待ちなどのtick単位よりも短いタイムアウトを使用したい場合、udelay()関数またはndelay()関数を使用する。
  • udelay()はloopの数をマイクロ秒で指定し、ndelay()はナノ秒で指定する。

6.6 時間管理関連のシステムコール

6.6.1 time()、gettimeofday()システムコール

  • time():1970/01/01 00:00:00(UTC)からの経過秒数を返す。(古いシステムコール)
  • gettimeofday():1970/01/01 00:00:00(UTC)からの経過秒数とマイクロ秒を返す。
  • ftime():1970/01/01 00:00:00(UTC)からの経過秒数を返す。(古いシステムコールで、ライブラリとして実装)
  • do_gettimeofday()は、xtime変数の値と使用可能なタイマ回路によって現在時刻を算出する。
  • settimeofday(), stime()システムコールで現在日時を設定できるが、RTCレジスタは変更されない。

6.6.2 adjtimex()システムコール

  • システム時刻はクロックドリフトによりずれていくが、急激な時刻の修正は障害を発生させる。
  • adjtimex()システムコールでtickごとに微修正を行うことができ、NTPなどが利用している。

6.6.3 setitimer()、alarm()システムコール

  • setitimer(), alarm()システムコールはユーザモードプロセスから利用可能なインターバルタイマ。
  • シグナルを送る周期(1回だけの場合は0)と次のシグナルを送るまでの残り時間を設定。
  • setitimer()の経過時間算出方式
    • ITIMER_REAL:実経過時間。CPUでプロセスを実行していないときにもシグナルを送る必要があるため、動的タイマを利用して実装されている。
    • ITIMER_VIRTUAL:ユーザモードで動作した時間。タイマ割り込みハンドラで実行。
    • ITIMER_PROF:ユーザモードおよびカーネルモードで動作した時間。
  • alarm()システムコールはプロセスディスクリプタの動的タイマを利用し、経過時間でシグナルを送る。

6.6.4 POSIXタイマ


  • POSIX 1003.1bで導入されたタイマをPOSIXタイマと呼ぶ。
  • ITIMER_REALとの相違点。
    • POSIXタイマではSIGALARMだけではなく任意のシグナルを送ることができる。
    • マルチスレッドアプリケーション全体に対しても、シングルスレッドに対してもシグナルを送ることができる。
    • アプリケーションのスレッドの通知関数を強制的に実行することもできるし、何もしないこともできる。
  • Linuxが提供するPOSIXタイマ
    • CLOCK_REALTIME:xtime変数を基にしたリアルタイムクロックであり、tickが1000Hzで動作していれば精度は999,848ナノ秒となる。
    • CLOCK_MONOTONIC:xtime変数から外部の時刻源との同期による時刻補正分を取り除いたリアルタイムクロック。精度はCLOCK_REALTIMEと同じ。

メンバーのみ編集できます