延迟和睡眠机制¶
本文档旨在回答一个常见问题:“插入延迟的正确方法 (TM) 是什么?”
驱动程序编写者最常面临这个问题,他们必须处理硬件延迟,并且可能不太熟悉 Linux 内核的内部工作原理。
下表给出了关于现有函数“族”及其限制的粗略概述。这个概述表不能代替使用前阅读函数描述!
*delay() |
usleep_range*() |
*sleep() |
||
---|---|---|---|---|
忙等待循环 |
基于 hrtimers |
基于定时器列表定时器 |
结合其他函数 |
|
在原子上下文中使用 |
是 |
否 |
否 |
否 |
在“短时间间隔”上精确 |
是 |
是 |
取决于 |
是 |
在“长时间间隔”上精确 |
不要使用! |
是 |
最大 12.5% 的延迟 |
是 |
可中断变体 |
否 |
是 |
是 |
否 |
对于非原子上下文,一个通用的建议是
如果不确定,请使用
fsleep()
(因为它结合了其他函数的所有优点)尽可能使用*sleep()
如果*sleep()的精度不够,请使用usleep_range*()
对于非常、非常短的延迟,请使用*delay()
在接下来的章节中查找有关函数“族”的更多详细信息。
*delay()函数族¶
这些函数使用时钟速度的 jiffy 估计值,并将忙等待足够的循环次数以达到所需的延迟。udelay()
是基本实现,ndelay()
以及mdelay()
是变体。
这些函数主要用于在原子上下文中添加延迟。在原子上下文中添加延迟之前,请务必问自己:这真的是必需的吗?
-
void udelay(unsigned long usec)¶
插入基于微秒的延迟,使用忙等待
参数
unsigned long usec
请求的延迟,以微秒为单位
描述
在原子上下文中延迟时,ndelay()
、udelay()
和mdelay()
是唯一有效的延迟/睡眠变体。
当在非原子上下文中插入的延迟短于队列 hrtimer 和进入调度器所需的时间时,使用udelay()
也是有价值的。但是,指定一个适用于所有系统的通用阈值并不简单。一个近似值是所有延迟高达 10 微秒的阈值。
当延迟大于架构特定的MAX_UDELAY_MS
值时,请确保使用mdelay()
。否则,存在溢出风险。
请注意,由于多种原因,ndelay()
、udelay()
和mdelay()
可能会提前返回(https://lists.openwall.net/linux-kernel/2011/01/09/56)
计算出的 loops_per_jiffy 太低(由于执行定时器中断所花费的时间)。
缓存行为影响执行循环函数所需的时间。
CPU 时钟速率变化。
-
void ndelay(unsigned long nsec)¶
插入基于纳秒的延迟,使用忙等待
-
mdelay¶
mdelay (n)
插入基于毫秒的延迟,使用忙等待
usleep_range*()和*sleep()函数族¶
这些函数使用 hrtimers 或定时器列表定时器来提供请求的睡眠持续时间。为了确定使用哪个函数是正确的,请考虑一些基本信息
hrtimers 更昂贵,因为它们使用 rb-tree(而不是哈希)
当请求的睡眠持续时间是第一个定时器时,hrtimers 更昂贵,这意味着必须对真实的硬件进行编程
定时器列表定时器总是提供某种延迟,因为它们是基于 jiffy 的
这里重复通用建议
如果不确定,请使用
fsleep()
(因为它结合了其他函数的所有优点)尽可能使用*sleep()
如果*sleep()的精度不够,请使用usleep_range*()
首先检查fsleep()
函数描述,要了解更多关于精度的信息,请检查msleep()
函数描述。
usleep_range*()¶
-
void usleep_range(unsigned long min, unsigned long max)¶
睡眠一段近似时间
参数
unsigned long min
睡眠的最短时间(以微秒为单位)
unsigned long max
睡眠的最长时间(以微秒为单位)
描述
有关基本信息,请参阅usleep_range_state()
。
任务在睡眠期间将处于 TASK_UNINTERRUPTIBLE 状态。
-
void usleep_range_idle(unsigned long min, unsigned long max)¶
睡眠一段近似时间,并进行空闲时间计算
参数
unsigned long min
睡眠的最短时间(以微秒为单位)
unsigned long max
睡眠的最长时间(以微秒为单位)
描述
有关基本信息,请参阅usleep_range_state()
。
睡眠任务在睡眠期间具有 TASK_IDLE 状态,以防止对负载平均做出贡献。
-
void usleep_range_state(unsigned long min, unsigned long max, unsigned int state)¶
以给定状态睡眠一段近似时间
参数
unsigned long min
睡眠的最短时间(以微秒为单位)
unsigned long max
睡眠的最长时间(以微秒为单位)
unsigned int state
睡眠时当前任务的状态
描述
usleep_range_state()
至少睡眠指定的最短时间,但不超过指定的最长时间。该范围可以通过允许 hrtimers 将已调度的中断与此 hrtimer 合并来减少功耗。在最坏的情况下,会为上限调度一个中断。
在开始睡眠之前,睡眠任务被设置为指定的状态。
在非原子上下文中,如果确切的唤醒时间是灵活的,请使用usleep_range()
或其变体,而不是udelay()
。睡眠通过避免 CPU 密集型的udelay()
忙等待来提高响应能力。
*sleep()¶
-
void msleep(unsigned int msecs)¶
即使有等待队列中断,也能安全睡眠
参数
unsigned int msecs
请求的睡眠持续时间(以毫秒为单位)
描述
msleep()
对睡眠持续时间使用基于 jiffy 的超时。由于定时器轮的设计,最大附加百分比延迟(延迟)为 12.5%。这仅对最终出现在定时器轮的级别 1 或更高级别的定时器有效。有关这 12.5% 的解释,请查看有关定时器轮基础知识的详细描述。
最终出现在级别 0 中的定时器的延迟取决于睡眠持续时间(毫秒)和 HZ 配置,并且可以通过以下方式计算(使用定时器轮设计限制,即延迟不小于 12.5%)
slack = MSECS_PER_TICK / msecs
当知道调用点的允许延迟时,可以反过来计算以找到满足约束的最小允许睡眠持续时间。例如
HZ=1000
withslack=25%
:MSECS_PER_TICK / slack = 1 / (1/4) = 4
: all sleep durations greater or equal 4ms will meet the constraints.HZ=1000
withslack=12.5%
:MSECS_PER_TICK / slack = 1 / (1/8) = 8
: all sleep durations greater or equal 8ms will meet the constraints.HZ=250
withslack=25%
:MSECS_PER_TICK / slack = 4 / (1/4) = 16
: all sleep durations greater or equal 16ms will meet the constraints.HZ=250
withslack=12.5%
:MSECS_PER_TICK / slack = 4 / (1/8) = 32
: all sleep durations greater or equal 32ms will meet the constraints.
另请参阅信号感知变体msleep_interruptible()
。
-
unsigned long msleep_interruptible(unsigned int msecs)¶
睡眠等待信号
参数
unsigned int msecs
请求的睡眠持续时间(以毫秒为单位)
描述
有关一些基本信息,请参阅msleep()
。
msleep()
和msleep_interruptible()
之间的区别在于,睡眠可能会被信号传递中断,然后提前返回。
返回
睡眠持续时间的剩余时间转换为毫秒(有关详细信息,请参阅 schedule_timeout())。
-
void ssleep(unsigned int seconds)¶
msleep 周围的秒包装器
-
void fsleep(unsigned long usecs)¶
灵活睡眠,自动选择最佳机制
参数
unsigned long usecs
请求的睡眠持续时间(以微秒为单位)
描述
flseep() 选择的最佳机制将为请求的睡眠持续时间提供最大 25% 的延迟。因此,它使用
udelay()
循环用于 <= 10 微秒的睡眠持续时间,以避免真正短的睡眠持续时间的 hrtimer 开销。usleep_range()
用于睡眠持续时间,如果使用msleep()
,会导致大于 25% 的延迟。这取决于 jiffies 的粒度。msleep()
用于所有其他睡眠持续时间。
注意
当未设置CONFIG_HIGH_RES_TIMERS
时,所有睡眠都以 jiffies 的粒度处理,并且延迟可能超过 25%,特别是对于短的睡眠持续时间。