Linuxには色々な時間変数がありますが、このjiffiesにハメられたので記録しておきます。(勝手にハマっただけ)
変数jiffies
Kernel起動時からtick(タイマー割込み)を数えた回数。
#define HZ
のCONFIG_HZの値で計算される。
仕事の環境では、HZ=200
だった。
jiffiesはHZ
の間隔でカウントアップされていく値である。
つまり、jiffiesの値をHZ
で割れば、secが算出されるハズ。
32bit環境
jiffies = 348,264 \\
HZ = 200\\
とした場合、\\
\begin{align}
jiffies\ / HZ &= 348,264\ /\ 200 \\
&= 1,741.3\ [sec] \\
&= 29.8\ [min]
\end{align}
この計算は、公式APIのunsigned int jiffies_to_msecs(const unsinged long j)
でも同じ計算をしている。
計算式自体は決して間違っていない。
しかし、64bit環境では思ったように答えが出ないのである。
64bit環境
jiffies = 4,295,130,087 \\
HZ = 200\\
とした場合、\\
\begin{align}
jiffies\ / HZ &= 4,795,130,087\ /\ 200 \\
&= 2,395,650.435\ [sec] \\
&= 39,927.50725\ [min]
\end{align}
は?20分ぐらいしか待ってないのに何それ?
jiffiesの初期値
このjiffies、初期値がクッソ特徴的である。
- 32bit環境 初期値-5分
- 64bit環境 32bitの初期値に、上位32bitを0埋め
\begin{align}
jiffies &= -5\ _{(10)}\ [min] \\
&= -6,000\ _{(10)}\ [sec] \\
&= FFFF\ E890\ _{(16)} \\
\end{align}\\
ただし、64bit環境では上位32bitを0埋めするため、\\
0000\ 0000\ FFFF\ E890\ _{(16)}
- 32bit環境の場合
FFFF E890
32bitの領域はこれでお終い。0000 0000
は32bit領域外。 - 64bit環境の場合
0000 0000 FFFF E890
64bit幅なので、上位32bit(0000 0000
)は0だが領域として存在している。
jiffiesは符号なし
こいつ、負数を初期値に取るクセしてunsigned型なのだ。
つまり、さっきの-5分はどんどんタイマーカウントを進めていって、5分後に綺麗に0000 0000 0000 0000
(16)になる。
メモリ幅による値の変化
5分後、jiffiesの値が綺麗に0になるのはいい。
だが、コンピューターにおける**加算して0になる**には注意が必要だ。
32bit幅の場合
結局、(0000 0000) FFFF E890
の羅列はtick毎に加算されて行って0になる。
その瞬間は(0000 0001) 0000 0000
となる。
だが、上位32bit(0000 0001
の部分)は32bit領域外なので消えてしまう。
みんな大好きオーバーフローだ。
その結果、32bitでは0000 0000
だけが残り、真に0となってしまう。
64bit幅の場合
0000 0000 FFFF E890
は加算され続けることで0になろうとするが、そうは問屋が卸さない。
0000 0001 0000 0000
となり、32bit幅では消えてしまった上位32bitの1が残り続ける。
つまり、
0000 0001 0000 0000
(16) = 4294967296
(10)
**0とはならない**のである。
64bit環境では、この値を引いてやらないと正しく計算できないわけだ。
再度、計算し直してみる。
jiffies = 4295130087\\
4294967296 < jiffiesを満たすため、補正処理を導入する。\\
jiffies - 4294967296 = 162791\\
\begin{align}\\
jiffies / HZ &= 162791 / 200\\
&=813.955\ [sec]\\
&=13.6\ [min]\\
\end{align}\\
さらに、開始が-5分であるため\\
18.6[min]
体感で20分ぐらい経ってから取得した値なので、それなりに合ってそう。
結論
みんな大好き、オーバーフローには気をつけようね!!(雑)