內核開發基礎-如何使用內核延時

從事Linux內核開發特別是驅動開發的小夥伴,肯定需要經常使用到定時器,比如,按鍵的去抖、LED屏幕顯存buffer的刷新等。同時,在控制硬件時,可能會用到十分精確地短延時,這時,定時器的精度就不能滿足這種需求了,這時就會使用到高精度定時器和忙等延時。今天就來簡要說一下如何正確的使用內核提供的delay和sleep函數。

這篇文章面對的讀者是從事與驅動程序開發,但是,對於內核delay和sleep實現機制不是很熟悉的開發人員。

如何插入delays

首先,你需要回答一個問題,“需要使用delay的代碼存在於原子性的上下文中嗎?”或者“是否真的需要在原子性的上下文中插入delay嗎?”。對於初學者來說,可能對於原子性的上下文不是很理解,下面簡要解釋一下。

對於任何Linux程序來說,無非運行於以下幾種上下文之中:1)進程-用戶空間上下文; 2)進程-內核空間上下文;3)硬件中斷上下文;4)軟件中斷上下文;其中,3)和4)爲原子性的上下文,其使用遵循以下幾種原則:

  1. 不允許訪問用戶空間。因爲其沒有處於任何進程上下文之中,而用戶空間都是依附於進程上下文的,所以,原子性上下文中,無法將任何進程與用戶空間關聯;

  2. 原子性的上下文中,使用current指針是沒有任何意義的。current指針指向當前的任務(進程或者線程),而原子性的上下文不與任何進程有關係;

  3. 不能執行任何休眠或者調度。原子性的上下文中,不能調用任何可能會引起進程切換、調度、休眠的函數,比如schedul、wait_event、kmalloc、copy_from_user、msleep、del_timer_sync等等。

原子性上下文

好了,解釋了原子性的上下文之後,我們言歸正傳,繼續說一下,原子性的上下文中延時函數都有哪些。

ndelay(unsigned long nsecs)
udelay(unsigned long usecs)
mdelay(unsigned long msecs)

這三個函數原理,都是通過使CPU處於忙等狀態,直到指定的指令週期執行完畢。其中,mdelay函數是udelay函數的簡單封裝。ndelay不是在所有平臺上都能達到ns的延時精度。

這三個函數一般用在需要延時很短時間的硬件驅動程序中,比如,等待網卡寄存器初始化完成,等待直流電機啓動完成等。

一般情況下,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)

除了udelay函數,其他函數都會使調用進程睡眠,從而被調度出去,這是合理的,因爲簡單的忙等會浪費大量的CPU時間,從而嚴重的降低系統性能。

使用慣例

對於不同的延時時間,使用何種延時函數,內核提供了一些慣例,下面一一說明一下。

延時幾微妙(< 10us)

使用udelay
  • 爲何選用udelay?
    對於嵌入式系統或者speed-steped PC,使用基於hrtimer實現的usleep,往往得不償失,不過,這依賴於具體的使用環境而定,不過,你需要注意到這一點。

延時幾毫秒(10us~20ms)

使用usleep_range
  • 爲何不使用msleep(1ms~20ms)?
    原始的解析,一般情況下,msleep(1ms20ms)往往不會達到使用者預期,其延時時長一般會稍微長於1ms20ms,這對精度十分敏感的應用來說是不可接受的。

  • 爲何不存在usleep函數?
    因爲usleep_range基於hrtimer實現,所以其有很好的精度,而usleep會引起大量的中斷,從而降低系統性能。

  • 什麼是合理的range?

    通過引入範圍,調度程序可以自由地將您的喚醒與由於其他原因而發生的任何其他喚醒結合在一起,或者在最壞的情況下,爲您的上限觸發中斷。

    您提供的範圍越大,則不會觸發中斷的機會就越大; 這應該與特定代碼路徑在延遲/性能上可接受的上限之間取得平衡。 此處的精確公差是非常具體的情況,因此,由調用者確定一個合理的範圍。

延時較大的毫秒(> 10ms)

使用msleep或者msleep_interruptible
  • msleep和msleep_interruptible的區別?

    msleep將當前任務設置爲TASK_UNINTERRUPTIBLE,而msleep_interruptible將當前任務設置爲TASK_INTERRUPTIBLE,然後調度睡眠。 簡而言之,區別在於睡眠是否可以通過信號提前結束。 通常,除非知道需要使用可中斷的變體,否則請使用msleep。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章