linux内核随机数初探

37 篇文章 9 订阅
15 篇文章 4 订阅

起Linux虚拟机时发现kernel日志random: crng init done之前等了很久。通过debug kernel代码对这个问题稍微有了一点理解,记录一下。

当前使用的kernel 代码是5.19.2.

drivers/char/random.c
static void __cold _credit_init_bits(size_t bits)
{
...
    pr_notice("crng init done\n");
...
}

dump stack找出caller function

[    3.731810] CPU: 0 PID: 1 Comm: systemd Tainted: G        W         5.19.2 #7
[    3.732393] Hardware name: linux,dummy-virt (DT)
[    3.732780] Call trace:
[    3.733016]  dump_backtrace.part.6+0xec/0xf8
[    3.733409]  show_stack+0x14/0x28
[    3.733722]  dump_stack_lvl+0x78/0x98
[    3.734062]  dump_stack+0x14/0x30
[    3.734374]  _credit_init_bits+0x1c/0x1ec
[    3.734740]  entropy_timer+0x50/0x60
[    3.735072]  call_timer_fn.isra.26+0x20/0x78
[    3.735463]  run_timer_softirq+0x4e4/0x578
[    3.735841]  _stext+0x11c/0x284
[    3.736138]  irq_exit_rcu+0x10c/0x138

...

本机上大部分的调用都是entropy_timer, 即大部分的熵的来源是时钟中断。注册timer在下面的code里。

static void __cold try_to_generate_entropy(void)
{
        enum { NUM_TRIAL_SAMPLES = 8192, MAX_SAMPLES_PER_BIT = HZ / 30 };
        struct entropy_timer_state stack;
        unsigned int i, num_different = 0;
        unsigned long last = random_get_entropy(); //read arm arch counter

        // decide samples_per_bit
        for (i = 0; i < NUM_TRIAL_SAMPLES - 1; ++i) {
                stack.entropy = random_get_entropy();
                if (stack.entropy != last)
                        ++num_different;
                last = stack.entropy;
        }
        stack.samples_per_bit = DIV_ROUND_UP(NUM_TRIAL_SAMPLES, num_different + 1);
        if (stack.samples_per_bit > MAX_SAMPLES_PER_BIT)
                return;

        stack.samples = 0;
        timer_setup_on_stack(&stack.timer, entropy_timer, 0);  // register timer
        while (!crng_ready() && !signal_pending(current)) {
                if (!timer_pending(&stack.timer))
                        mod_timer(&stack.timer, jiffies + 1);   // at least get sample every 2 jiffies
                mix_pool_bytes(&stack.entropy, sizeof(stack.entropy));
                schedule();
                stack.entropy = random_get_entropy();
        }

        del_timer_sync(&stack.timer);
        destroy_timer_on_stack(&stack.timer);
        mix_pool_bytes(&stack.entropy, sizeof(stack.entropy));
}

上面的代码主要作用是注册一个新的定时器,决定多久调用一次定时器。如果当前这个新的定时器没有处于pending状态就会在下一个时钟中断再去trigger。通过打印,entropy_timer的调用频率大约是2个jiffies一次。此外samples_per_bit也会对采样频率产生影响。这个值的大小跟当前机器的arm arch timer的频率有关。系统时钟频率越高此值越大。

static void __cold entropy_timer(struct timer_list *timer)
{
        struct entropy_timer_state *state = container_of(timer, struct entropy_timer_state, timer);

        if (++state->samples == state->samples_per_bit) {
                credit_init_bits(1);
                state->samples = 0;
        }
}

#define credit_init_bits(bits) if (!crng_ready()) _credit_init_bits(bits)

并非每次调用entropy_timer都会触发采样。samples_per_bit决定调用credit_init_bits的频率。根据测试在系统时钟是200M的机器上samples_per_bit的值是1,也就是每次都会调用credit_init_bits.在系统时钟频率是25M的机器上samples_per_bit是2,也即每两次entropy_timer调用,调用一次credit_init_bits.

根据测试,采样的用数量大约在250次左右,不同机器差异不大,如此,影响随机数初始化时间的因素包括硬件:系统时钟的频率; 软件:时钟中断的频率,由kenrel config配置。此外增加熵的来源也能大大提高初始化速度。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值