「Linux/時計」の版間の差分

提供: Wikinote
移動: 案内検索
(2 つの時計)
(タイマ割り込み処理)
 
(同じ利用者による、間の9版が非表示)
行2: 行2:
  
 
== 2 つの時計 ==
 
== 2 つの時計 ==
 +
Linux だけでなく PC では、2 つの時計を用いて、電源の供給がない場合でも
 +
時計がずれないように配慮されている。
 +
 
; ハードウェアクロック (リアルタイムクロック; RTC)
 
; ハードウェアクロック (リアルタイムクロック; RTC)
: マザーボードに実装されたチップで、電源がオフでも進み続ける。精度はそれほどよくない。
+
: マザーボードに実装されたチップで、電源の供給がなくてもバッテリで進み続ける。精度はそれほどよくないため、OS 起動中は、基本的には参照されない。
 
; ソフトウェアクロック (システムクロック)
 
; ソフトウェアクロック (システムクロック)
 
: カーネルが管理している、タイマ割り込みによって進められる時計 (カウンタ)。もちろん電源オフ時は消滅する。調節すれば、かなりの高精度になる。
 
: カーネルが管理している、タイマ割り込みによって進められる時計 (カウンタ)。もちろん電源オフ時は消滅する。調節すれば、かなりの高精度になる。
行15: 行18:
 
# シャットダウン時にシステムクロックを RTC に書き戻す。
 
# シャットダウン時にシステムクロックを RTC に書き戻す。
 
# 1. に戻る
 
# 1. に戻る
 +
 +
=== 起動時・終了時の動作 ===
 +
 +
起動時の RTC からシステムクロックへの読み込みは、/etc/rc.d/rc.sysinit で行われる。
 +
 +
# /etc/sysconfig/clock を読み込み
 +
# <code>$CLOCKFLAGS</code> に <code>--hctosys</code> を設定
 +
# <code>UTC=yes|true</code> ならば、<code>$CLOCKFLAGS</code> に <code>--utc</code> を設定。<code>UTC=no|false</code> ならば、<code>--localtime</code> を設定
 +
# 以下で hwclock を実行
 +
 +
[ -x /sbin/hwclock ] && /sbin/hwclock $CLOCKFLAGS
 +
 +
action $"Setting clock $CLOCKDEF: `date`" /bin/true
 +
 +
終了時は /etc/rc.d/init.d/halt で、起動時とほぼ同様の動作が行われる。
 +
<code>--hctosys</code> が <code>--systohc</code> になるだけである。
 +
 +
[ -x /sbin/hwclock ] && action $"Syncing hardware clock to system time" /sbin/hwclock $CLOCKFLAGS
 +
 +
=== タイマ割り込み処理 ===
 +
 +
timer_interrupt() @ arch/i386/kernel/time.c あたりから。
 +
 +
# xtime_lock のロックを取る。
 +
# do_timer_interrupt_hook() @ include/asm-i386/mach-default/do_timer.h
 +
#: tick_divider の回数だけ以下の処理を繰り返す。デフォルトは 1 であり、設定しない限り 1 回のみ。
 +
## do_timer() @ kernel/timer.c
 +
### jiffies_64 をインクリメント。
 +
### update_times()
 +
#### ticks = jiffies - wall_jiffies
 +
#### wall_jiffies += ticks
 +
#### update_wall_time()
 +
#### calc_load(ticks) — ロードアベレージの計算
 +
## update_process_times() @ kernel/timer.c
 +
### モードによって current のユーザー時間あるいはシステム時間を増加させる。 各 CPU 毎に用意されている cpustat の情報も更新する。
 +
### run_local_timers()
 +
#### raise_softirq() — ソフト割り込み
 +
#### softlockup_tick() — soft lockup 検出処理
 +
#### scheduler_tick() — tick 毎のスケジューラ処理
 +
### run_posix_cpu_timers() — タイマー処理
 +
## profile_tick() @ kernel/profile.c — プロファイリングのための処理やフックがある。
 +
# xtime_lock をアンロック。
 +
 +
; tick_divider
 +
: カーネルパラメータ divider で設定する。divider=10 を設定すると、タイマ割り込みの回数が 1/10 になる。このため、上記のように 1 回の割り込みで 10 回分のタイマ割り込み処理を行なう必要があるようだ。具体的には、jiffies は通常通り 1 秒間に 1000 増加するが (増加の粒度が荒くなり、10 ずつ一気に増えるイメージ)、/proc/interrupts の timer の値は 1 秒間に 100 しか増加しなくなる。VMware などが 1000Hz の割り込みを再現できず(?) 時計がずれる場合などに有効らしい。なんか強引で気持ち悪いね。
 +
 +
[root@centos47 stap]# cat /proc/cmdline
 +
ro root=LABEL=/ clock=pmtmr divider=10
 +
[root@centos47 stap]# while :; do grep timer /proc/interrupts ; sleep 1 ; done
 +
  0:    647556    IO-APIC-edge  timer
 +
  0:    647657    IO-APIC-edge  timer
 +
  0:    647758    IO-APIC-edge  timer
 +
[root@centos47 stap]# ./jiffies.stp ← jiffies の値を表示する SystemTap スクリプト
 +
Tue Mar 30 11:35:47 2010 jiffies=6444870
 +
Tue Mar 30 11:35:48 2010 jiffies=6445870
 +
Tue Mar 30 11:35:49 2010 jiffies=6446870
 +
 +
== 関連用語 ==
 +
; jiffies 変数
 +
: タイマ割り込みの回数を記録する。jiffies_64 の下位 32 ビットが jiffies。グローバルタイマ割り込みハンドラ do_timer() にてインクリメントされる。
 +
; HZ マクロ
 +
: 1 秒間に発生するタイマ割り込みの回数。通常、RHEL では 1000、SLES では 250 に設定されている模様。
 +
; xtime 変数
 +
: 1970 年 1 月 1 日 0 時 0 分 0 秒からの経過秒数。グローバルタイマ割り込みハンドラ do_timer() にて進められる。timespec 型の構造体で、tv_sec には経過秒数が格納され、tv_nsec には tv_sec からのナノ秒単位の経過時間が格納される。
 +
 +
== 参考文献 ==
 +
* [http://www.cis.udel.edu/~mills/database/rfc/rfc1589.txt RFC 1589: A Kernel Model for Precision Timekeeping]

2010年3月30日 (火) 20:39時点における最新版

Linux でどのように時計が動いているのか。

2 つの時計

Linux だけでなく PC では、2 つの時計を用いて、電源の供給がない場合でも 時計がずれないように配慮されている。

ハードウェアクロック (リアルタイムクロック; RTC)
マザーボードに実装されたチップで、電源の供給がなくてもバッテリで進み続ける。精度はそれほどよくないため、OS 起動中は、基本的には参照されない。
ソフトウェアクロック (システムクロック)
カーネルが管理している、タイマ割り込みによって進められる時計 (カウンタ)。もちろん電源オフ時は消滅する。調節すれば、かなりの高精度になる。

この 2 つの時計は、以下のような関係で動作している。 Clock.png

  1. 電源オフ時、RTC のみが時間を刻んでいる。
  2. 起動時に OS が RTC の時刻を読み取り、以後タイマ割り込みでシステムクロックを刻む。
  3. NTP 等を利用していて時刻が正確だとわかると、11 分ごとにシステムクロックを RTC に書き戻す。
  4. シャットダウン時にシステムクロックを RTC に書き戻す。
  5. 1. に戻る

起動時・終了時の動作

起動時の RTC からシステムクロックへの読み込みは、/etc/rc.d/rc.sysinit で行われる。

  1. /etc/sysconfig/clock を読み込み
  2. $CLOCKFLAGS--hctosys を設定
  3. UTC=yes|true ならば、$CLOCKFLAGS--utc を設定。UTC=no|false ならば、--localtime を設定
  4. 以下で hwclock を実行
[ -x /sbin/hwclock ] && /sbin/hwclock $CLOCKFLAGS

action $"Setting clock $CLOCKDEF: `date`" /bin/true

終了時は /etc/rc.d/init.d/halt で、起動時とほぼ同様の動作が行われる。 --hctosys--systohc になるだけである。

[ -x /sbin/hwclock ] && action $"Syncing hardware clock to system time" /sbin/hwclock $CLOCKFLAGS

タイマ割り込み処理

timer_interrupt() @ arch/i386/kernel/time.c あたりから。

  1. xtime_lock のロックを取る。
  2. do_timer_interrupt_hook() @ include/asm-i386/mach-default/do_timer.h
    tick_divider の回数だけ以下の処理を繰り返す。デフォルトは 1 であり、設定しない限り 1 回のみ。
    1. do_timer() @ kernel/timer.c
      1. jiffies_64 をインクリメント。
      2. update_times()
        1. ticks = jiffies - wall_jiffies
        2. wall_jiffies += ticks
        3. update_wall_time()
        4. calc_load(ticks) — ロードアベレージの計算
    2. update_process_times() @ kernel/timer.c
      1. モードによって current のユーザー時間あるいはシステム時間を増加させる。 各 CPU 毎に用意されている cpustat の情報も更新する。
      2. run_local_timers()
        1. raise_softirq() — ソフト割り込み
        2. softlockup_tick() — soft lockup 検出処理
        3. scheduler_tick() — tick 毎のスケジューラ処理
      3. run_posix_cpu_timers() — タイマー処理
    3. profile_tick() @ kernel/profile.c — プロファイリングのための処理やフックがある。
  3. xtime_lock をアンロック。
tick_divider
カーネルパラメータ divider で設定する。divider=10 を設定すると、タイマ割り込みの回数が 1/10 になる。このため、上記のように 1 回の割り込みで 10 回分のタイマ割り込み処理を行なう必要があるようだ。具体的には、jiffies は通常通り 1 秒間に 1000 増加するが (増加の粒度が荒くなり、10 ずつ一気に増えるイメージ)、/proc/interrupts の timer の値は 1 秒間に 100 しか増加しなくなる。VMware などが 1000Hz の割り込みを再現できず(?) 時計がずれる場合などに有効らしい。なんか強引で気持ち悪いね。
[root@centos47 stap]# cat /proc/cmdline 
ro root=LABEL=/ clock=pmtmr divider=10
[root@centos47 stap]# while :; do grep timer /proc/interrupts ; sleep 1 ; done
  0:     647556    IO-APIC-edge  timer
  0:     647657    IO-APIC-edge  timer
  0:     647758    IO-APIC-edge  timer
[root@centos47 stap]# ./jiffies.stp ← jiffies の値を表示する SystemTap スクリプト
Tue Mar 30 11:35:47 2010 jiffies=6444870
Tue Mar 30 11:35:48 2010 jiffies=6445870
Tue Mar 30 11:35:49 2010 jiffies=6446870

関連用語

jiffies 変数
タイマ割り込みの回数を記録する。jiffies_64 の下位 32 ビットが jiffies。グローバルタイマ割り込みハンドラ do_timer() にてインクリメントされる。
HZ マクロ
1 秒間に発生するタイマ割り込みの回数。通常、RHEL では 1000、SLES では 250 に設定されている模様。
xtime 変数
1970 年 1 月 1 日 0 時 0 分 0 秒からの経過秒数。グローバルタイマ割り込みハンドラ do_timer() にて進められる。timespec 型の構造体で、tv_sec には経過秒数が格納され、tv_nsec には tv_sec からのナノ秒単位の経過時間が格納される。

参考文献