/dev/randomと/dev/urandom
/dev/randomと/dev/urandom(これで最後)は、乱数を発生させるデバイスで、エントロピ生成機と称する入力デバイス(/proc/input下のデバイス)割り込みタイミングをベースに、乱数を発生させるようです。なお/dev/randomは完全にエントロピ生成機をベースにしており、従ってマウス/キーボードの入力割り込みがない場合、その割り込みが発生するまで、ブロックされます。 /dev/randomをcatで読み出すと、キーボード/マウスを動作させないと停まってしまうのはそのためです。(キーボード/マウスの操作はリモート端末ではダメです。)
なお、割り込みハンドらのstruct irqaction* action->flagsにIRQF_SAMPLE_RANDOMを設定することで、入力デバイス以外の割り込みデバイスでも、乱数のベースにすることができるようです。
/dev/urandomもエントロピ生成機をベースにしていますが、割り込みが発生していない場合、前回の情報をベースにハッシュにて乱数を生成しているようです。従ってブロックされることがありません。
入力割り込み毎に前回の割り込みとの時間差をベースにして、エントロピープールに蓄えて、実際の乱数発生時は、このプールからセコンドプールに転送して、そこから乱数を生成しているようです。たぶんメインプールに蓄え時は、プリエンプと禁止する必要があり、パフォーマンスの兼ね合いじゃないでしょうか?この辺りのこととか、数学的(?)な事もあってか、実装はわかりません。
とりあえず、エントロピーのベースとなる、割り込みからの処理です。入力デバイスからの割り込み毎に、add_input_randomness()がコールされ、そこからadd_timer_randomness()してエントロピーの計算をしているようです。
なお、割り込みハンドらのstruct irqaction* action->flagsにIRQF_SAMPLE_RANDOMを設定することで、入力デバイス以外の割り込みデバイスでも、乱数のベースにすることができるようです。
/dev/urandomもエントロピ生成機をベースにしていますが、割り込みが発生していない場合、前回の情報をベースにハッシュにて乱数を生成しているようです。従ってブロックされることがありません。
入力割り込み毎に前回の割り込みとの時間差をベースにして、エントロピープールに蓄えて、実際の乱数発生時は、このプールからセコンドプールに転送して、そこから乱数を生成しているようです。たぶんメインプールに蓄え時は、プリエンプと禁止する必要があり、パフォーマンスの兼ね合いじゃないでしょうか?この辺りのこととか、数学的(?)な事もあってか、実装はわかりません。
とりあえず、エントロピーのベースとなる、割り込みからの処理です。入力デバイスからの割り込み毎に、add_input_randomness()がコールされ、そこからadd_timer_randomness()してエントロピーの計算をしているようです。
void add_input_randomness(unsigned int type, unsigned int code, unsigned int value) { static unsigned char last_value; /* ignore autorepeat and the like */ if (value == last_value) return; DEBUG_ENT("input event\n"); last_value = value; add_timer_randomness(&input_timer_state, (type << 4) ^ code ^ (code >> 4) ^ value); }なんか良くわかりませんが、雰囲気、前回の割り込みと今回の割り込みの時間差(jiffies値)をdelta、前回のdeltaと今回のdeltaの差をdelta2、前回のdelta2と今回のdelta2の差をdelta3とし、その絶対値の小さい物を、credit_entropy_bit()の引数として、エントロピーの処理としているようですが・・・。
static void add_timer_randomness(struct timer_rand_state *state, unsigned num) { struct { long jiffies; unsigned cycles; unsigned num; } sample; long delta, delta2, delta3; preempt_disable(); /* if over the trickle threshold, use only 1 in 4096 samples */ if (input_pool.entropy_count > trickle_thresh && ((__this_cpu_inc_return(trickle_count) - 1) & 0xfff)) goto out; sample.jiffies = jiffies; if (!arch_get_random_int(&sample.cycles)) sample.cycles = get_cycles(); sample.num = num; mix_pool_bytes(&input_pool, &sample, sizeof(sample)); if (!state->dont_count_entropy) { delta = sample.jiffies - state->last_time; state->last_time = sample.jiffies; delta2 = delta - state->last_delta; state->last_delta = delta; delta3 = delta2 - state->last_delta2; state->last_delta2 = delta2; if (delta < 0) delta = -delta; if (delta2 < 0) delta2 = -delta2; if (delta3 < 0) delta3 = -delta3; if (delta > delta2) delta = delta2; if (delta > delta3) delta = delta3; credit_entropy_bits(&input_pool, min_t(int, fls(delta>>1), 11)); } out: preempt_enable(); }