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

Timer 为什么udelay和ndelay在linux内核中不准确?,timer,linux-kernel,linux-device-driver,delay,Timer,Linux Kernel,Linux Device Driver,Delay,我做了一个这样的函数 trace_printk("111111"); udelay(4000); trace_printk("222222"); trace_printk("111111"); ndelay(10000); ndelay(10000); ndelay(10000); ndelay(10000); .... ....//totally 400 ndelay calls trace_printk("222222"); 日志显示是4.01毫秒,没问题 但是当我这样打电话的时候 tr

我做了一个这样的函数

trace_printk("111111");
udelay(4000);
trace_printk("222222");
trace_printk("111111");
ndelay(10000);
ndelay(10000);
ndelay(10000);
ndelay(10000);
....
....//totally 400 ndelay calls
trace_printk("222222");
日志显示是4.01毫秒,没问题

但是当我这样打电话的时候

trace_printk("111111");
udelay(4000);
trace_printk("222222");
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/100000000
。该值实际上约为4.29,但被四舍五入为5。这是一个相当严重的错误


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

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

但是,使用0x4将导致
ndelay
小于请求的纳秒数。一般来说,延迟函数的目标是提供至少与用户请求的时间一样长的延迟(请参见此处:)。

您可以使用ktime\u get\n()获得启动后的高精度时间。因此,您不仅可以将其用作高精度延迟,还可以用作高精度计时器。例如:

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
}
u64t;
t=ktime_get_ns();//自启动后获取当前纳秒数
对于(i=0;i<24;i++)//通过GPIO发送24 1200ns-1300ns脉冲
{
gpio_设置_值(引脚,1);//驱动gpio或执行其他操作
t+=1200;//现在我们有了下一步的绝对时间
while(ktime_get_ns()
因为延迟结束时另一个进程正在执行,它需要先完成它的时间片。我禁用了中断,这与调度无关。而这种延迟与cpu周期相比,即使进程休眠,也不会影响结果