@
目錄- 中斷上半部、下半部的概念
- 實作中斷下半部的三種方法
- 軟中斷
- 軟中斷模版
- tasklet
- tasklet函式模版
- 作業佇列
- 作業佇列函式模版
- 行程背景關系和中斷背景關系
- 軟中斷和硬中斷的區別
- 硬中斷、軟中斷和信號的區別
中斷上半部、下半部的概念
??設備的中斷會打斷內核行程中的正常調度和運行,系統對更高吞吐率的追求勢必要求中斷服務程式盡量短小精悍,但是,這個良好的愿望往往與現實并不吻合,在大多數真實的系統中,當中斷到來時,要完成的作業往往并不會是短小的,它可能要進行較大量的耗時處理,
??下圖描述了Linux內核的中斷處理機制,為了在中斷執行時間盡量短和中斷處理需完成的作業盡量大之間找到一個平衡點,Linux將中斷處理程式分解為兩個半部:頂半部和底半部,

??頂半部用于完成盡量少的比較緊急的功能,它往往只是簡單地讀取暫存器中的中斷狀態,并在清除中斷標志后就進行“登記中斷”的作業,“登記中斷”意味著將底半部處理程式掛到該設備的底半部執行佇列中去,這樣,頂半部執行的速度就會很快,從而可以服務更多的中斷請求,
??現在,中斷處理作業的重心就落在了底半部的頭上,需用它來完成中斷事件的絕大多數任務,底半部幾乎做了中斷處理程式所有的事情,而且可以被新的中斷打斷,這也是底半部和頂半部的最大不同,因為頂半部往往被設計成不可中斷,底半部相對來說并不是非常緊急的,而且相對比較耗時,不在硬體中斷服務程式中執行,
??盡管頂半部、底半部的結合能夠善系統的回應能力,但是,僵化地認為Linux設備驅動中的中斷處理一定要分兩個半部則是不對的,如果中斷要處理的作業本身很少,則完全可以直接在頂半部全部完成,
??其他作業系統中對中斷的處理也采用了類似于 Linux的方法,真正的硬體中斷服務程式都斥盡量短,因此,許多作業系統都提供了中斷背景關系和非中斷背景關系相結合的機制,將中斷的耗時作業保留到非中斷背景關系去執行,
實作中斷下半部的三種方法
軟中斷
??軟中斷( Softirq)也是一種傳統的底半部處理機制,它的執行時機通常是頂半部回傳的時候, tasklet是基于軟中斷實作的,因此也運行于軟中斷背景關系,
??在Linux內核中,用 softing_action結構體表征一個軟中斷,這個結構體包含軟中斷處理函式指標和傳遞給該函式的引數,使用 open_softirq()函式可以注冊軟中斷對應的處理函式,而 raise_softirq()函式可以觸發一個軟中斷,
??軟中斷和 tasklet運行于軟中斷背景關系,仍然屬于原子背景關系的一種,而作業佇列則運行于行程背景關系,因此,在軟中斷和 tasklet處理函式中不允許睡眠,而在作業佇列處理函式中允許睡眠,
??local_bh_disable()和 llocal_bh_enable()是內核中用于禁止和使能軟中斷及 tasklet底半部機制的函式
軟中斷模版
asmlinkage void do_softirq(void)
{
__u32 pending;
unsigned long flags;
/* 判斷是否在中斷處理中,如果正在中斷處理,就直接回傳 */
if (in_interrupt())
return;
/* 保存當前暫存器的值 */
local_irq_save(flags);
/* 取得當前已注冊軟中斷的位圖 */
pending = local_softirq_pending();
/* 回圈處理所有已注冊的軟中斷 */
if (pending)
__do_softirq();
/* 恢復暫存器的值到中斷處理前 */
local_irq_restore(flags);
}
tasklet
??tasklet的使用較簡單,它的執行背景關系是軟中斷,執行時機通常是頂半部回傳的時候,我們只需要定義 tasklet及其處理函式,并將兩者關聯則可,例如
void my_tasklet_func(unsigned long); /*定義一個處理函式*/
DECLARE_TASKLET(my_tasklet, my_tasklet_func, data);
/*定義一個tasklet結構my_tasklet,與my_tasklet_func(data)函式相關聯*/
??代碼DECLARE_TASKLET(my_tasklet,my_tasklet_func,data)實作了定義名稱為my_tasklet的tasklet,并將其與my_tasklet_func()這個函式系結,而傳入這個函式的引數為data,
在需要調度tasklet的時候參考一個tasklet_schedule()函式就能使系統在適當的時候進行調度運行:
tasklet_schedule(&my_tasklet);
??使用tasklet作為底半部處理中斷的設備驅動程式模板下所示(僅包含與中斷相關的部
分),
tasklet函式模版
/* 定義tasklet和底半部函式并將它們關聯 */
void xxx_do_tasklet(unsigned long);
DECLARE_TASKLET(xxx_tasklet, xxx_do_tasklet, 0);
/* 中斷處理底半部 */
void xxx_do_tasklet(unsigned long)
...
/* 中斷處理頂半部 */
irqreturn_t xxx_interrupt(int irq, void *dev_id)
{
...
tasklet_schedule(&xxx_tasklet);
...
}
/* 設備驅動模塊加載函式 */
int __init xxx_init(void)
{
...
/* 申請中斷 */
result = request_irq(xxx_irq, xxx_interrupt,
0, "xxx", NULL);
...
return IRQ_HANDLED;
}
/* 設備驅動模塊卸載函式 */
void __exit xxx_exit(void)
{
...
/* 釋放中斷 */
free_irq(xxx_irq, xxx_interrupt);
...
}
??上述程式在模塊加載函式中申請中斷(第24~25行),并在模塊卸載函式free_irq(xxx_irq, xxx_interrupt);中釋放它,對應于xxx_irq的中斷處理程式被設定為xxx_interrupt()函式,在這個函式中,tasklet_schedule(&xxx_tasklet)調度被定義的tasklet函式xxx_do_tasklet()在適當的時候執行,
作業佇列
??作業佇列的使用方法和tasklet非常相似,但是作業佇列的執行背景關系是內核執行緒,因此可以調度和睡眠,下面的代碼用于定義一個作業佇列和一個底半部執行函式
struct work_struct my_wq; /* 定義一個作業佇列 */
void my_wq_func(struct work_struct *work); /* 定義一個處理函式 */
??通過INIT_WORK()可以初始化這個作業佇列并將作業佇列與處理函式系結:
INIT_WORK(&my_wq, my_wq_func);
/* 初始化作業佇列并將其與處理函式系結 */
??與tasklet_schedule()對應的用于調度作業佇列執行的函式為schedule_work(),如:
schedule_work(&my_wq); /* 調度作業佇列執行 */
作業佇列函式模版
/* 定義作業佇列和關聯函式 */
struct work_struct xxx_wq;
void xxx_do_work(struct work_struct *work);
/* 中斷處理底半部 */
void xxx_do_work(struct work_struct *work)
...
/*中斷處理頂半部*/
irqreturn_t xxx_interrupt(int irq, void *dev_id)
{
...
schedule_work(&xxx_wq);
...
return IRQ_HANDLED;
}
/* 設備驅動模塊加載函式 */
int xxx_init(void)
{
...
/* 申請中斷 */
result = request_irq(xxx_irq, xxx_interrupt,
0, "xxx", NULL);
...
/* 初始化作業佇列 */
INIT_WORK(&xxx_wq, xxx_do_work);
...
}
/* 設備驅動模塊卸載函式 */
void xxx_exit(void)
{
...
/* 釋放中斷 */
free_irq(xxx_irq, xxx_interrupt);
...
}
??作業佇列早期的實作是在每個CPU核上創建一個worker內核執行緒,所有在這個核上調度的作業都在該worker執行緒中執行,其并發性顯然差強人意,在Linux 2.6.36以后,轉而實作“Concurrency-managedworkqueues”,簡稱cmwq,cmwq會自動維護作業佇列的執行緒池以提高并發性,同時保持了API的向后兼容,
行程背景關系和中斷背景關系
談談行程背景關系、中斷背景關系及原子背景關系的一些概念
軟中斷和硬中斷的區別
硬中斷:
??1. 硬中斷是由硬體產生的,比如,像磁盤,網卡,鍵盤,時鐘等,每個設備或設備集都有它自己的IRQ(中斷請求),基于IRQ,CPU可以將相應的請求分發到對應的硬體驅動上(注:硬體驅動通常是內核中的一個子程式,而不是一個獨立的行程),
??2. 處理中斷的驅動是需要運行在CPU上的,因此,當中斷產生的時候,CPU會中斷當前正在運行的任務,來處理中斷,在有多核心的系統上,一個中斷通常只能中斷一顆CPU(也有一種特殊的情況,就是在大型主機上是有硬體通道的,它可以在沒有主CPU的支持下,可以同時處理多個中斷,),
??3. 硬中斷可以直接中斷CPU,它會引起內核中相關的代碼被觸發,對于那些需要花費一些時間去處理的行程,中斷代碼本身也可以被其他的硬中斷中斷,
??4. 對于時鐘中斷,內核調度代碼會將當前正在運行的行程掛起,從而讓其他的行程來運行,它的存在是為了讓調度代碼(或稱為調度器)可以調度多任務,
軟中斷:
??1. 軟中斷的處理非常像硬中斷,然而,它們僅僅是由當前正在運行的行程所產生的,
??2. 通常,軟中斷是一些對I/O的請求,這些請求會呼叫內核中可以調度I/O發生的程式,對于某些設備,I/O請求需要被立即處理,而磁盤I/O請求通常可以排隊并且可以稍后處理,根據I/O模型的不同,行程或許會被掛起直到I/O完成,此時內核調度器就會選擇另一個行程去運行,I/O可以在行程之間產生并且調度程序通常和磁盤I/O的方式是相同,
??3. 軟中斷僅與內核相聯系,而內核主要負責對需要運行的任何其他的行程進行調度,一些內核允許設備驅動的一些部分存在于用戶空間,并且當需要的時候內核也會調度這個行程去運行,
??4. 軟中斷并不會直接中斷CPU,也只有當前正在運行的代碼(或行程)才會產生軟中斷,這種中斷是一種需要內核為正在運行的行程去做一些事情(通常為I/O)的請求,有一個特殊的軟中斷是Yield呼叫,它的作用是請求內核調度器去查看是否有一些其他的行程可以運行,
硬中斷、軟中斷和信號的區別
??硬中斷是外部設備對CPU的中斷,軟中斷是中斷底半部的一種處理機制,而信號則是由內核(或其他行程)對某個行程的中斷,在涉及系統呼叫的場合,人們也常說通過軟中斷(例如ARM為swi)陷入內核,此時軟中斷的概念是指由軟體指令引發的中斷,和我們這個地方說的softirq是兩個完全不同的概念,一個是software,一個是soft,
??需要特別說明的是,軟中斷以及基于軟中斷的tasklet如果在某段時間內大量出現的話,內核會把后續軟中斷放入ksoftirqd內核執行緒中執行,總的來說,中斷優先級高于軟中斷,軟中斷又高于任何一個執行緒,軟中斷適度執行緒化,可以緩解高負載情況下系統的回應,
有任何問題,均可通過公告中的二維碼聯系我
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/242655.html
標籤:嵌入式
下一篇:Makefile常用的函式
