为什么 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
}
我做了一个这样的函数
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
}