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分ぐらい経ってから取得した値なので、それなりに合ってそう。
結論
みんな大好き、オーバーフローには気をつけようね!!(雑)