時間は無限?
マシンを立ち上げるとLinuxカーネルが立ち上がります。そして、マシンを停止するまでカーネルが動きつづけるわけです。この間、カーネルは刻々と時を刻んでいきます。
そこでカーネルでの時間の管理に使われるのが、jiffiesという変数です。
kernel-2.4では、kernel/timer.cで次のように定義されています。
----
unsigned long volatile jiffies __cacheline_aligned;
----
unsigned longなので、32bit環境では、0〜0xffffffffの範囲を表すことができます。
このjiffiesは、timer割り込みと呼ばれる処理の際に1ずつ増加していきます。timer割り込みの頻度は、"HZ"として定数で定義されています。
linux-2.4.21/include/asm-i386/param.h
----
#ifndef HZ
#define HZ 100
#endif
----
ということで、kernel-2.4のIA32環境では、1秒間に100回のタイマー割り込みが発生し、jiffiesが1秒間に100増えていくということになります。
それでは、jiffiesの値は無限に増やせるのでしょうか?
さきほど示したように、32bit環境では、jiffiesの最大値は0xffffffffなので、その時間までしかカウントできないことになります。計算してみると、約497日(1年5ヶ月)で最大値を迎えることになります。これが噂のjiffiesのオーバーフローです。jiffiesがオーバーフローすると、また0に戻ってカウントしていきます。
さて、jiffiesがオーバーフローすると何か困ることがあるでしょうか?
例えば、今の時間がjiffies変数に入っていて、n秒後にタイムアウトを判断するために、次のようなコードを書いたとします。
----
timeout = jiffies + n;
タイマーセット(timeout);
----
数秒後に、タイムアウト時間が過ぎているかどうか調べてみます。
----
if (jiffies < timeout){
printk("まだタイムアウトしてないよ!\n");
}
----
こんなコードを書いた時、「噂のjiffiesのオーバーフロー」が発生するとどうなるでしょう? timeoutが32bitの最大値ぎりぎり(例えば 0xfffffff9)に設定されたりしていると、オーバーフロー直後にチェックすると、
-----
if ( 3 < 0xffffffff9 )
→ 真なので、「まだタイムアウトしてないよ!」出力。
-----
といったことになって、正しく処理できなくなってしまいます。
そこで、カーネルではそんな悲しい出来事が起きないように、ちょっとした工夫をしています。
それはここです。
linux-2.4.21/include/sched.h
-----
#define time_after(a,b) \
(typecheck(unsigned long, a) && \
typecheck(unsigned long, b) && \
((long)(b) - (long)(a) < 0))
----
jiffiesのようなタイムアウトの判断に使うためのマクロが用意されています。ポイントはここですね。
----
((long)(b) - (long)(a) < 0))
----
こういう計算にすると、jiffiesがオーバーフローしても大丈夫ですね。ポイントは「キャスト」です。(キャストといっても装甲が飛んで軽くなるあれ系とは関係ありません。)
kernel 2.4の多くのコードがこのマクロを使うように書き換えられていますが、まだまだ完璧ではないようです。ちなみにkernel 2.6ではjiffiesが64bit化されているので、オーバーフローまで何日かかることやら...。

コメント