延遲和睡眠機制¶
本文件旨在回答一個常見問題:“插入延遲的正確方法 (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=1000withslack=25%:MSECS_PER_TICK / slack = 1 / (1/4) = 4: all sleep durations greater or equal 4ms will meet the constraints.HZ=1000withslack=12.5%:MSECS_PER_TICK / slack = 1 / (1/8) = 8: all sleep durations greater or equal 8ms will meet the constraints.HZ=250withslack=25%:MSECS_PER_TICK / slack = 4 / (1/4) = 16: all sleep durations greater or equal 16ms will meet the constraints.HZ=250withslack=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%,特別是對於短的睡眠持續時間。