一、內核定時器1.基本概念
在個別場景下,我們須要在特定的時間後做個別動作,而且又不想仍然等待而浪費CPU,這個時侯定時器是十分合適的機制。定時器用於在將來的某個時間點執行某個函數以完成特定的任務。
內核定時器告訴內核在指定的時間點使用特定的參數來呼叫特定的函數。定時器是非同步運行於其註冊者的,定時器運行時,註冊該定時器的任務可能在休眠也可能在其它處理器上運行,甚至可能早已退出。
linux中核心定時器是基於(軟體)中斷實作的linux 應用計時器,也就是它處於中斷上下文而非進程上下文。在非進程上下文有些原則要遵守:
不准許訪問用戶空間
current無意義,因此也不可用
不能進行睡眠或則調度。不能呼叫schedule或則某種wait_event,也不能呼叫任何可能造成睡眠的函數。訊號量也不可用,由於訊號量可能導致休眠。
內核程式碼透過呼叫函數in_interrupt()可以判定目前是否處於中斷上下文,只要回傳非0就表示處於中斷上下文。核心可以透過呼叫in_atomic()來判定目前是否容許調度。不容許調度的情況包括:處於中斷上下文以及擁有載子鎖的上下文。
因為定時器是非同步執行的,因此定時器處理函數必須注意進行互斥保護。
2.linux核心支援的定時器
linux核心支援兩種類型的定時器:
精典定時器:精確度取決於電腦時鐘中斷運作頻度的定時器。此定時器精度通常比較低,為1000/HZms的精度。此定時器以固定的頻度形成,即每隔1000/HZms形成一次。若果實沒有使能動態時鐘特點,則在該定時器到期時,可能並沒有真正的定時風波發生,例如係統中只添加瞭如下定時器:11ms,52ms,78ms到期的定時器,而且精典定時器會在4的倍數ms處到期(4,8,12...),因此說定時器到期的時間點不一定有定時風波發生。
高幀率定時器:精典定時器的精度比較低,在有的場合須要更高精度的定時器,例如多媒體應用中文linux操作系統,因此系統引入了該類型的定時器。此定時器本質上可以在任意時刻發生。
這兒也須要非常之處兩個概念:
動態時鐘:只有在有任務須要實際執行時,才啟動週期時鐘linux 應用定時器,否則就禁用週期時鐘的技術。作法是假如須要調度idle來運行,禁用週期時鐘;直至下一個定時器到期為止或則有中斷發生時為止再啟用週期時鐘。單觸發時鐘是實現動態時鐘的前提條件,由於動態時鐘的關鍵特徵是可以按照須要來停止或重啟時鐘,而純粹的周期時鐘不適用於這些場景。
週期時脈:週期性的形成時脈時間的時脈。
從應用上來說,定時器主要有兩個用途:
超時:表示在一定時間後會發生的風波,事實上,在使用超時時,大多數情形下,並不期望超時時間發生,定時器常常會在超時之前被取消。另外雖然不取消,超時風波也常常不是一個精確的風波,例如在網路中用到的各類超時定時器,它們抒發的意思常常是假如在這個時間點之前還沒有...,則可以覺得……,這個時間的取值常常是一個經驗值或則計算值,並不是精確的時間要求。在這些場合精典定時器是夠用的。
定時器:用於實現時序,例如播放聲音時,須要定期往聲卡發送數據,這些場合下,對時間就有比較嚴格的要求,倘若不在某個時間點發送數據到聲卡,還會出現聲音失真。這時就要使用高精度定時器。
透過配置可以促使linux工作在以下模式:
高幀率動態時脈
高幀率週期時鐘
低碼率動態時鐘
低碼率週期時鐘
3.低碼率內核定時器
低碼率定時器是最常見的核心定時器,核心會使用處理器的時脈中斷或則其他任何適當的週期性時脈源作為定時器的時間基線。時鐘中斷會定期發生,每秒HZ次。此中斷對應的定時器處理函數通常為timer_interrupt,在該函數的處理中,最終會調到do_timer和update_process_timers。其中do_timer會負責全系統範圍的、全域的任務:更新jiffies,處理進程統計;而後一個函數則會進行進程統計,形成TIMER_SOFTIRQ,向調度器提供時間感知。
當定時器到期時,在定時器處理函數被呼叫之前,定時器會被從激活數組移走,因此假如想要在本次執行完後再過一段時間後重新執行,就須要重新添加定時器。在SMP系統中linux中文亂碼,定時器函數會由註冊它的CPU執行。
內核定時器的實作要滿足以下要求和假定:
定時器管理必須盡可能簡化.
其設計必須在活動定時器大量降低時具有挺好的伸縮性
大部份定時器在幾秒或最多幾分鐘內到期,而帶有長延時的定時器是相當稀少.
一個定時器應該在註冊它的同一個CPU上運行.
低碼率內核定時器的實作是很巧妙的。它基於一個每-CPU資料結構。 timer_list的base數組包含了指向該結構的表針。假如base是NULL,這個定時器沒有被呼叫運行;否則,這個表針告知那個資料結構(也就是那個CPU)正在運行它。
無論何時核心程式碼註冊一個定時器(透過add_timer或則mod_timer),操作最終由internal_add_timer執行(在kernel/timer.c),它會將新的定時器新增至和目前CPU關聯的「級聯表”中的定時器單向數組中。
級聯表的工作方法:
假如定時器在接出來的0到255jiffies內到期,則它被添加到特供短時定時器的256個數組中的一個,使用expires(即添加到哪個數組是由到期時間的比特位決定的)的低8位決定加到哪個數組中
假如它在將來更久時間到期(但又在16384個jiffies之前),則它被添加到64個數組之一,這64個數組與expires的8-13比特位相關,expires的這6個比特決定了使用那個陣列。
類似的技術被應用於expires的位元組14-19,20-25以及26-31。
假如定時器在更久的經來到期
以上是深入了解 Linux 核心定時器:基於中斷的非同步機制與非進程上下文原則的詳細內容。更多資訊請關注PHP中文網其他相關文章!