中斷處理機制與工作隊列

一、中斷處理機制

      中斷是一個隨機事件,因此如果關中斷的時間過長,CPU就不能及時的響應其他的中斷請求,從而造成中斷的丟失。因此,Linux內核的目標就是儘可能快的處理完中斷請求,儘可能的把更多的處理向後推遲。因此,內核把中斷分爲了兩部分:上半部和下半部,上半部(就是中斷服務程序),下半部(就是一些內核函數)留着稍後處理。首先,一個快速的”上半部”來處理硬件發出的請求,它必須在一個新的中斷產生之前終止。其次,“下半部”運行時是允許中斷請求的,而上半部運行時是關中斷的。這是二者的主要區別。

1.中斷“下半部”

內核到底什麼時候執行“下半部”,以何種方式組織“下半部”?

下半部實現機制中,在老的內核中,這個機制叫做bottom-half(BH),但是linux的這種BH機制有兩個缺點:

(1)  在任一時刻,系統中只能有一個CPU可以執行BH代碼,以防止兩個或多個CPU同時來執行BH函數而相互干擾,因此BH代碼的執行是嚴格“串行化的”

(2)  BH函數不允許嵌套

而2.6內核以後,“下半部”機制處理機制有了很大的發展和改進:

(1)  軟中斷請求機制

(2)  小任務機制

(3)  工作隊列機制

2.軟中斷

      整個softirq機制的設計與實現中自始自終貫徹了一個思想:“誰觸發,誰執行”,即觸發軟中斷的那個CPU負責執行它所觸發的中斷,而且每個CPU都有它自己的軟中斷觸發與控制機制。

2.小任務機制

tasklet機制是一種較爲特殊的軟中斷。

特點:

(1)與一般的軟中斷不同,某一段tasklet代碼在某個時刻只能在一個CPU上運行,而不像一般的軟中斷服務函數(即softirq_action結構體中的action函數指針)那樣------在同一時刻可以被多個CPU併發地執行。

(2)與BH機制不同,不同的tasklet代碼在同一時刻可以在多個CPU上併發地執行,而不像BH機制那樣必須嚴格地串行化執行。

linux中用tasklet_struct來描述一個tasklet,每個結構體代表一個獨立的小任務

注:tasklet_struct結構體中atomic_t count成員表示對這個tasklet的引用計數值,只有當count爲0時,tasklet代碼段才能執行,如果count非零,則這個tasklet是被禁止的。任何想要執行一個tasklet代碼段的人都首先必須檢查其count成員是否爲0.

linux在interrupt.h頭文件中定義了兩個宏來定義tasklet_struct 結構變量的輔助宏:

DECLARE_TASKLET(name,func,data)和DECLARE_TASKLET_DISABLED(name,func,data),顯然,DECLARE_TASKLET宏在初始化時是被使能的,因爲其count爲0,而DECLARE_TASKLET_DISABLED是被禁止的,因爲其count爲1.

 

小任務機制總結:

(1)  聲明和使用小任務大多數情況下,爲了控制一個常用的硬件設備,小任務機制是實現下半部的最佳選擇。小任務可以動態創建,使用方便,執行起來也比較快,也可以靜態創建。選擇哪種方式取決於到底是想對小任務進行直接引用還是間接引用,如果準備靜態地創建一個小任務(直接引用),使用下面兩個宏中的一個:

DECLARE_TASKLET(name,func,data)

DECLARE_TASKLET_DISABLED(name,func,data)

(2)  編寫自己的小任務處理程序,小任務處理程序必須符合如下的函數類型:

void tasklet_handler(unsigned long data)

由於小任務不能睡眠,因此不能在小任務中使用信號量或者其他產生阻塞的函數。但是小任務運行時可以響應中斷

(3)  調度自己的小任務通過調用tasklet_schedule()函數

 

3.工作隊列

      工作隊列是另外一種將工作推後執行的形式,它和tasklet有所不同。工作隊列可以把工作推後,交由一個內核線程去執行,也就是說這個下半部可以在進程的上下文中執行。這樣,通過工作隊列執行的代碼能佔盡進程上下文的所有優勢。最重要的是工作隊列允許被重新調度甚至是睡眠。

那麼,什麼情況下使用工作隊列,什麼情況下使用tasklet呢?

如果推後執行的任務需要睡眠,那麼就選擇工作隊列;

如果推後執行的任務不需要睡眠,那麼就選擇tasklet。

另外,如果需要用一個可以重新調度的實體來執行你的下半部處理,也可以使用工作隊列。它是唯一能在進程上下文運行的下半部實現的機制,也只有它纔可以睡眠。這意味着在需要獲得大量的內存時、在需要獲取信號量時,在需要執行阻塞式的IO操作時,它都會非常有用。如果不需要用一個內核線程來推後執行工作,那麼就考慮使用tasklet。

我們把推後執行的任務叫做工作,描述工作的結構體是work_struct,這些工作以隊列結構組織成工作隊列,描述工作隊列的結構體爲workqueue_struct,而工作線程就是負責執行工作隊列中的工作,系統默認的工作線程是events,自己也可以創建自己的工作者線程。

二、工作隊列詳解

Linux中workqueue機制

爲什麼需要workqueue?Linux中的workqueue機制就是爲了簡化內核線程的創建。通過調用workqueue的接口就能創建內核線程。並且可以根據當前系統CPU的個數創建線程的數量,使得線程處理的事務能夠並行化。

 

Linux內核創建工作隊列時:




__alloc_workqueue_key函數會調用kthread_create來進行線程的創建

這個線程以wq爲數據執行rescuer_thread這個函數。

 

觸摸屏驅動工作隊列:

沒有使用系統默認的工作隊列keventd_wq(這個工作隊列是在Linux系統初始化的時候就創建的),因爲默認的工作隊列存在性能問題。

在初始化workqueue過程中,內核需要初始化內核線程來處理工作隊列,註冊的內核線程工作比較簡單,就是不斷的掃描對應的cpu_workqueue_stuct中的任務隊列,從中獲取一個有效任務,然後執行該任務。

內核線程的創建調用create_singlethread_workqueue(name)來完成,該函數會創建一個工作隊列,並且在內部創建了一個內核線程。

 

工作隊列的創建方式有兩種,如下圖所示:


第二種方式中,會創建自己的工作隊列,優點是性能高,會創建內核線程來處理工作函數。

發佈了32 篇原創文章 · 獲贊 35 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章