delay/sleep mechanism in Linux kernel
一、How to insert delays in kernel
在编写device driver时,经常需要加一段延时以等待hardware操作完成,在linux kernel中有udelay,也有sleep, 对不同时长的延时,该怎么选择呢?选择合适的延时函数,能够较好地平衡scheduler和power management的需求。
首先也是最重要的,明确当前是不是在atomic context中,我们知道,在atomic context下,是不允许休眠的。接下来,我们就分别从atomic和un-atomic context来介绍适用的延时函数。
1.1 atomic context
在atomic context下,不能休眠,只能使用busy wait的*delay函数来实现一段延时操作。这些*delay函数包括:
ndelay(unsigned long nsecs);
udelay(unsigned long usecs);
mdelay(unsigned long msecs);
这里面最推荐使用udelay,在一些non-PC platforms中,可能不支持ndelay;mdelay是基于udelay来实现的,通常需要重新调整code,来使用msleep来完成较长的延时。
这主要是考虑到power需求,毕竟ms级的busy-wait延时,对省电不是很友好。
1.2 un-atomic context
在un-atomic context中,没有休眠的限制,可以使用*sleep[_range]系列的函数,通常是使用定时器设置一个定时时间,之后就可以让出CPU去处理其他任务,定时到期之后,会唤醒该task继续执行。
*sleep[_range]函数按照所使用的定时器的不同,可以分成以下两类:
1)基于hrtimer的延时
usleep_range (unsigned long min, unsigned long max)
设置hrtimer最早在min时刻,最晚在max时刻叫醒任务。
2)基于jiffies/legacy_timer的延时函数
msleep (unsigned long msecs)
msleep_interruptible(unsigned long msecs)
这两者的区别是,msleep在做schedule之前,会设置task为TASK_UNINTERRUPTIBLE,不允许被其他signal打断;
而msleep_interruptible设置task为TASK_INTERRUPTIBLE,允许被其他signal打断,这就有可能在timeout之前唤醒。
注意到,这里的msleep有可能因为线程调度,延迟更长的时间。
这三类函数的适用场景
延时场景 | 函数 |
<10us | udelay |
10us~20ms | usleep_range() |
>10ms | msleep() |