一、中斷下半部-工作佇列
1、中斷
先看一下宋寶華先生的《linux裝置驅動開發詳解》裡面對中斷的描述吧。這本書個人感覺 寫的比較好,從開始學驅動到現在,還能從中得到不少知識。
裝置的中斷會打斷核心中程序的正常排程和執行,系統對更高吞吐率的追求勢必要求中斷服務程式盡可能地短小精悍。但是,這個良好的願望往往與現實並不吻合。在大多數真實的系統中,當中斷到來時,要完成的工作往往並不會是短小的,它可能要進行較大量的耗時處理。如下圖描述了linux核心的中斷處理機制。為了在中斷執行時間盡可能短和中斷處理需完成大量工作之間找到乙個平衡點,linux將中斷處理程式分解為兩個半部:頂半部(top half)和底半部(bottom half)。頂半部完成盡可能少的比較緊急的功能,它往往只是簡單地讀取暫存器中的中斷狀態並清除中斷標誌後就進行「登記中斷」的工作。「登記中斷」意味著將底半部處理程式掛到該裝置的底半部執行佇列中去。這樣,頂半部執行的速度就會很快,可以服務更多的中斷請求。現在,中斷處理工作的重心就落在了底半部的頭上,它來完成中斷事件的絕大多數任務。底半部幾乎做了中斷處理程式所有的事情,而且可以被新的中斷打斷,這也是底半部和頂半部的最大不同,因為頂半部往往被設計成不可中斷。底半部則相對來說並不是非常緊急的,而且相對比較耗時,不在硬體中斷服務程式中執行。儘管頂半部、底半部的結合能夠改善系統的響應能力,但是,僵化地認為linux裝置驅動中的中斷處理一定要分兩個半部則是不對的。如果中斷要處理的工作本身很少,則完全可以直接在頂半部全部完成。
其實上面這一段大致說明乙個問題,那就是:中斷要盡可能耗時比較短,盡快恢復系統正常除錯,所以把中斷觸發、中斷執行分開,也就是所說的「上半部分(中斷觸發)、底半部(中斷執行)」,其實就是我們後面說的中斷上下文。下半部分一般有tasklet、工作佇列實現,觸控螢幕中中斷實現以工作佇列形式實現的,所以我們今天重點講工作佇列。
2、為什麼還需要工作佇列?
工作佇列(work queue)是另外一種將中斷的部分工作推後的一種方式,它可以實現一些tasklet不能實現的工作,比如工作佇列機制可以睡眠。這種差異的本質原因是,在工作佇列機制中,將推後的工作交給乙個稱之為工作者執行緒(worker thread)的核心執行緒去完成(單核下一般會交給預設的執行緒events/0)。因此,在該機制中,當核心在執行中斷的剩餘工作時就處在程序上下文(process context)中。也就是說由工作佇列所執行的中斷**會表現出程序的一些特性,最典型的就是可以重新排程甚至睡眠。
對於tasklet機制(中斷處理程式也是如此),核心在執行時處於中斷上下文(interrupt context)中。而中斷上下文與程序毫無瓜葛,所以在中斷上下文中就不能睡眠。因此,選擇tasklet還是工作佇列來完成下半部分應該不難選擇。當推後的那部分中斷程式需要睡眠時,工作佇列毫無疑問是你的最佳選擇;否則,還是用tasklet吧。
3、中斷上下文
有上面那個圖可以看出上下兩部分吧,就是上下文吧,這個比較好理解。
看下別人比較專業的解釋:
在了解中斷上下文時,先來回顧另乙個熟悉概念:程序上下文(這個中文翻譯真的不是很好理解,用「環境」比它好很多)。一般的程序執行在使用者態,如果這個程序進行了系統呼叫,那麼此時使用者空間中的程式就進入了核心空間,並且稱核心代表該程序執行於核心空間中。由於使用者空間和核心空間具有不同的位址對映,並且使用者空間的程序要傳遞很多變數、引數給核心,核心也要儲存使用者程序的一些暫存器、變數等,以便系統呼叫結束後回到使用者空間繼續執行。這樣就產生了程序上下文。
所謂的程序上下文,就是乙個程序在執行的時候,cpu的所有暫存器中的值、程序的狀態以及堆疊中的內容。當核心需要切換到另乙個程序時(上下文切換),它需要儲存當前程序的所有狀態,即儲存當前程序的程序上下文,以便再次執行該程序時,能夠恢復切換時的狀態繼續執行。上述所說的工作佇列所要做的工作都交給工作者執行緒來處理,因此它可以表現出程序的一些特性,比如說可以睡眠等。 對於中斷而言,是硬體通過觸發訊號,導致核心呼叫中斷處理程式,進入核心空間。這個過程中,硬體的一些變數和引數也要傳遞給核心,核心通過這些引數進行中斷處理,中斷上下文就可以理解為硬體傳遞過來的這些引數和核心需要儲存的一些環境,主要是被中斷的程序的環境。因此處於中斷上下文的tasklet不會有睡眠這樣的特性。
4、工作佇列的使用方法
核心中通過下述結構體來表示乙個具體的工作:
struct work_struct
;
而這些工作(結構體)鏈結成的鍊錶就是所謂的工作佇列。工作者執行緒會在被喚醒時執行鍊錶上的所有工作,當乙個工作被執行完畢後,相應的work_struct結構體也會被刪除。當這個工作鍊錶上沒有工作時,工作執行緒就會休眠。
(1)、通過如下巨集可以建立乙個要推後的完成的工作:
declare_work(name,void(*func)(void*),void*data);
(2)、也可以通過下述巨集動態建立乙個工作:
init_work(struct work_struct*work,void(*func)(void*),void *data);
與tasklet類似,每個工作都有具體的工作佇列處理函式,原型如下:
void work_handler(void *data)
將工作佇列機制對應到具體的中斷程式中,即那些被推後的工作將會在func所指向的那個工作佇列處理函式中被執行。
(3)、實現了工作佇列處理函式後,就需要schedule_work函式對這個工作進行排程,就像這樣:
schedule_work(&work);
這樣work會馬上就被排程,一旦工作執行緒被喚醒,這個工作就會被執行(因為其所在工作佇列會被執行)。 中斷上下文 程序上下文
在學習與作業系統相關的知識時候,我們經常遇到程序上下文 中斷上下文,看似熟悉又感覺不是特別清晰。這裡我們從如下幾個方面進行描述。上下文是從英文中context翻譯過來的,指的是一種環境。上下文我們看起來不怎麼熟悉,但是我們可以看context的中文翻譯,或者我們能更加的情形些。context n 語...
linux中斷 程序上下文和中斷上下文
中斷發生以後,cpu跳到核心設定好的中斷處理 中去,由這部分核心 來處理中斷。這個處理過程中的上下文就是中斷上下文。為什麼可能導致睡眠的函式都不能在中斷上下文中使用呢?首先睡眠的含義是將程序置於 睡眠 狀態,在這個狀態的程序不能被排程執行。然後,在一定的時機,這個程序可能會被重新置為 執行 狀態,從...
程序上下文和中斷上下文
程序上下文和中斷上下文是作業系統中很重要的兩個概念,這兩個概念在作業系統課程中不斷被提及,是最經常接觸 看上去很懂但又說不清楚到底怎麼回事。造成這種局面的原因,可能是原來接觸到的作業系統課程的教學總停留在一種淺層次的理論層面上,沒有深入去研究。處理器總處於以下狀態中的一種 核心態,執行於程序上下文,...