这篇文档提供了关于在Linux内核中使用不同延迟/休眠机制的信息,主要面向需要处理硬件延迟但对Linux内核内部工作机制不够熟悉的驱动程序编写人员。
插入延迟
首先需要问自己的问题是:“我的代码是否处于原子上下文中?”接着紧随其后的问题是:“它真的需要在原子上下文中延迟吗?”如果需要的话...
原子上下文:
- 必须使用*delay函数族。这些函数使用jiffie估计的时钟速度,并将忙等待足够的循环周期以实现所需的延迟:
ndelay(unsigned long nsecs)
udelay(unsigned long usecs)
mdelay(unsigned long msecs)
- 通常首选udelay API;在许多非PC设备上可能实际上不存在ndelay级别的精度。
- mdelay是udelay的宏包装器,用于处理向udelay传递大参数时可能发生的溢出。一般来说,不建议使用mdelay,应重新设计代码以允许使用msleep。
非原子上下文:
-
应该使用*sleep[_range]函数族。这里有更多的选项,虽然它们中的任何一个都可能正常工作,但使用“正确”的休眠函数将有助于调度程序、电源管理,并使您的驱动程序更好 ?
-
由忙等待循环支持:
udelay(unsigned long usecs)
-
由高精度定时器(hrtimers)支持:
usleep_range(unsigned long min, unsigned long max)
-
由jiffies / legacy_timers支持:
msleep(unsigned long msecs)
msleep_interruptible(unsigned long msecs)
-
与*delay函数族不同,驱动每个调用的底层机制都不同,因此您应该了解其中的一些特殊情况。
休眠“几”微秒(< ~10us?):
- 使用udelay
- 为什么不使用usleep?
- 在较慢的系统上(嵌入式系统,或者可能是速度递减的PC!),为usleep设置高精度定时器的开销可能不值得。这样的评估显然取决于您的具体情况,但这是需要注意的事项。
休眠~微秒或小毫秒(10us - 20ms):
- 使用usleep_range
- 为什么不使用msleep(1ms - 20ms)?
- 在这里有原始解释:链接
- msleep(120)可能不会产生调用者期望的效果,并且通常会休眠更长时间(对于120ms范围内给定的任何值,实际休眠时间可能约为20ms)。在许多情况下,这不是期望的行为。
- 为什么没有“usleep” / 什么是一个好的范围?
- 由于usleep_range是建立在高精度定时器之上的,唤醒将非常精确(大致如此),因此简单的usleep函数可能会引入大量不需要的中断。
- 通过引入一个范围,调度程序可以自由地将您的唤醒与可能由于其他原因发生的任何其他唤醒合并,或者在最坏的情况下,为您的上限触发中断。
- 您提供的范围越大,您不触发中断的机会就越大;这应该与您的特定代码路径的延迟/性能的可接受上限相平衡。这里的确切容忍度非常具体,因此由调用者确定一个合理的范围。
休眠更长的毫秒数(10ms+)
- 使用msleep或可能是msleep_interruptible
- 有什么区别?
- msleep将当前任务设置为TASK_UNINTERRUPTIBLE,而msleep_interruptible在调度休眠之前将当前任务设置为TASK_INTERRUPTIBLE。简而言之,区别在于休眠是否可以被信号提前结束。一般来说,除非您知道需要可中断的变体,否则只需使用msleep。
灵活的休眠(任意延迟,不可中断)
- 使用fsleep
以上是关于在Linux内核中使用不同延迟/休眠机制的详细信息。