为什么udelay和ndelay在linux内核中不准确?

时间:2015-05-22 12:34:17

标签: timer linux-kernel linux-device-driver delay

我做了这样的功能

trace_printk("111111");
udelay(4000);
trace_printk("222222");

并且日志显示它是4.01毫秒,它是'OK

但是当我这样打电话时

trace_printk("111111");
ndelay(10000);
ndelay(10000);
ndelay(10000);
ndelay(10000);
....
....//totally 400 ndelay calls
trace_printk("222222");

日志将显示4.7毫秒。这是不可接受的。 为什么ndelay的错误是如此巨大?

深入了解内核代码,我发现了这两个函数的实现

void __udelay(unsigned long usecs)
{
    __const_udelay(usecs * 0x10C7UL); /* 2**32 / 1000000 (rounded up) */
}

void __ndelay(unsigned long nsecs)
{
    __const_udelay(nsecs * 0x5UL); /* 2**32 / 1000000000 (rounded up) */
}

我认为udelay将是ndelay的1000倍,但它不是,为什么?

3 个答案:

答案 0 :(得分:2)

正如您已经注意到的那样,纳秒延迟实现与毫秒延迟相比是一个非常粗略的近似,因为使用了0x5常数因子。 0x10c7 / 0x5约为859.使用0x4将接近1000(约1073)。

但是,使用0x4会导致ndelay 小于请求的纳秒数。通常,只要用户请求,延迟函数的目的就是提供至少的延迟(参见此处:http://practicepeople.blogspot.jp/2013/08/kernel-programming-busy-waiting-delay.html)。

答案 1 :(得分:1)

每次调用它时,都会添加舍入错误。请注意评论2**32 / 1000000000。这个值真的是~4.29,但它被四舍五入到5.这是一个非常大的错误。

相比之下,udelay错误很小:(~4294.97对4295 [0x10c7])。

答案 2 :(得分:0)

您可以使用ktime_get_ns()获得自启动以来的高精度时间。因此,您不仅可以将其用作高精度延迟,还可以将其用作高精度计时器。有一个例子:

u64 t;
t = ktime_get_ns(); // Get current nanoseconds since boot
for (i = 0; i < 24; i++) // Send 24 1200ns-1300ns pulses via GPIO
{
    gpio_set_value(pin, 1); // Drive GPIO or do something else
    t += 1200; // Now we have absolute time of the next step
    while (ktime_get_ns() < t); // Wait for it
    gpio_set_value(pin, 0); // Do something, again
    t += 1300; // Now we have time of the next step, again
    while (ktime_get_ns() < t);  // Wait for it, again
}