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

Why udelay and ndelay is not accurate in linux kernel?

我做了一个这样的函数

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

日志显示为 4.01 毫秒,没问题

但是当我这样打电话时

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倍,结果不是,为什么?

每次调用都会加上一个舍入误差。注意注释 2**32 / 1000000000。该值实际上是 ~4.29,但四舍五入为 5。这是一个相当大的错误。

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

正如您已经注意到的那样,由于使用了 0x5 常数因子,与毫秒延迟相比,纳秒延迟实现是相当粗略的近似值。 0x10c7 / 0x5 大约是 859。使用 0x4 会更接近 1000(大约 1073)。

但是,使用 0x4 会导致 ndelay 小于 请求的纳秒数。一般来说,延迟功能旨在提供延迟 至少 只要用户请求(参见此处:http://practicepeople.blogspot.jp/2013/08/kernel-programming-busy-waiting-delay.html)。

您可以使用 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
}