ISR Interrupt Service Routines(中斷服務程式)
INTC Interrupt controller(中斷控制器)
Linux中通常分為外部中斷(又叫硬體中斷)和内部中斷(又叫異常)
軟體對硬體進行配置後,軟體期望等待硬體的某種狀态(比如,收到了資料),這裡有兩種方式,
一種是輪詢(polling): CPU 不斷的去讀硬體狀态。另一種是當硬體完成某種事件後,給 CPU
一個中斷,讓 CPU 停下手上的事情,去處理這個中斷。很顯然,中斷的互動方式提高了系統的吞吐。
當 CPU 收到一個中斷 (IRQ)的時候,會去執行該中斷對應的處理函數(ISR)。普通情況下,
會有一個中斷向量表,向量表中定義了 CPU 對應的每一個外設資源的中斷處理程式的入口,當
發生對應的中斷的時候, CPU 直接跳轉到這個入口執行程式。也就是中斷上下文。(注意:中斷上下文中,不可阻塞睡眠)。
在Linux中希望盡快完成ISR,但是有些ISR事務繁重,會消耗很多時間,導緻反應速度變差,linux針對這種情況,分了上半部,底半部。
1. 上半部(top half):收到一個中斷,立即執行,有嚴格的時間限制,隻做一些必要的工作,比如:應答,複位等。這些工作都是在所有中斷被禁止的情況下完成的。
2. 底半部(bottom half):能夠被推遲到後面完成的任務會在底半部進行。在适合的時機,下半部會被開中斷執行。
相關API:
申請注冊一個中斷處理函數:
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
參數 含義
irq 表了該中斷的中斷号,一般 CPU 的中斷号都會事先定義好。
handler 中斷發生後的 ISR
flags 中斷标志( IRQF_DISABLED / IRQFSAMPLE_RANDOM / IRQF_TIMER / IRQF_SHARED)
标志 含義
IRQF_DISABLED 設定這個标志的話,意味着核心在處理這個 ISR 期間,要禁止其他中斷(多數情況不使用這個)
IRQFSAMPLE_RANDOM 表明這個裝置産生的中斷對核心熵池有貢獻
IRQF_TIMER 為系統定時器準備的标志
IRQF_SHARED 表明多個中斷處理程式之間共享中斷線。同一個給定的線上注冊每個處理程式,必須設定這個
name 中斷相關的裝置 ASCII 文本,例如 "keyboard",這些名字會在 /proc/irq 和 /proc/interrupts 檔案使用
dev 用于共享中斷線,傳遞驅動程式的裝置結構。非共享類型的中斷,直接設定成為 NULL
return:0 success -EBUSY表示中斷線已使用或者沒有指定IRQF_SHARED
注意:request_irq 函數可能引起睡眠,是以不允許在中斷上下文或者不允許睡眠的代碼中調用。
釋放中斷
const void *free_irq(unsigned int irq, void *dev_id)
實作底半部的三種方法:tasklet,等待隊列,軟中斷
軟中斷:很少用于實作下半部,但是tasklet是軟中斷實作的。軟中斷就是軟體實作的異步中斷,優先級低于硬體中斷,高于普通程序。
軟中斷是在編譯時候靜态配置設定的,要用軟中斷就要修改核心代碼。
實作軟中斷的相關結構體和函數:
struct softirq_action
{
void (*action)(struct softirq_action *); //軟中斷處理函數
};
enum //軟中斷的優先級别
{
HI_SOFTIRQ=0, //用于tasklet的軟中斷,優先級最高,為0
TIMER_SOFTIRQ, //定時器的下半部
NET_TX_SOFTIRQ, //發送網絡資料的軟中斷
NET_RX_SOFTIRQ, //接受網絡資料的軟中斷
BLOCK_SOFTIRQ,
TASKLET_SOFTIRQ, //也是用于實作tasklet
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ,
XIAOBAI_SOFTIRQ, //這是自添加軟中斷的一個中斷号,優先級最低
NR_SOFTIRQS, //這個就是上面所說的軟中斷結構體數組成員個數
};
static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp; //聲明軟中斷的一個表
void open_softirq(int nr, void (*action)(struct softirq_action *)) //申請注冊一個軟中斷(中斷号綁定執行函數)
注意:如果想自添加軟中斷,需要導出符将open_softirq導出,在其他驅動中可以調用。EXPORT_SYMBOL(open_softirq);
void raise_softirq(unsigned int nr) //觸發對應的軟中斷,參數為枚舉的中斷号XIAOBAI_SOFTIRQ
注意:如果想自添加軟中斷,需要導出符将raise_softirq導出,在其他驅動中可以調用。EXPORT_SYMBOL(raise_softirq);
實作流程為:
1.在include/linux/interrupt.h中添加自己的軟中斷号XIAOBAI_SOFTIRQ(在RCU_SOFTIRQ後添加)
2.将kernel/softirq.c中open_softirq和raise_softirq兩個函數使用導出符EXPORT_SYMBOL導出。
3.申請一個軟中斷并綁定處理函數 open_softirq(XIAOBAI_SOFTIRQ,handler_function);
4.在中斷處理函數中執行下半部,也就是軟中斷。raise_softirq(XIAOBAI_SOFTIRQ);
軟中斷補充:所謂的觸發軟中斷,并不是立刻觸發,他隻是告訴核心,下次執行軟中斷的時候,記得執行我的軟中斷處理函數。
軟中斷優先級最高的中斷号就是tasklet。
tasklet:
和軟中斷一樣,觸發函數執行後并不是立刻執行,隻是告訴核心,下次執行軟中斷的時候執行我的處理函數。
實作方法:
1. struct tasklet_struct test_tasklet; //定義tasklet結構體
2. tasklet_init(&test_tasklet, test_func, (unsigned long)123); //test_func為處理函數,123為給處理函數傳的參數
3. tasklet_schedule(&test_tasklet);或者tasklet_hi_schedule(&test_tasklet);//中斷傳回前排程tasklet
前者使用TASKLET_SOFTIRQ優先級,後者使用HI_SOFTIRQ優先級。
4.移除tasklet的API:void tasklet_kill(struct tasklet_struct *t)//如果tasklet在運作,程式會休眠,等到它執行完畢。
還有禁止和激活tasklet的函數:(禁止後,tasklet就不能用了,直到激活)
static inline void tasklet_disable(struct tasklet_struct *t) //禁止
static inline void tasklet_enable (struct tasklet_struct *t) //激活
補充:軟中斷實作tasklet的中斷号優先級有TASKLET_SOFTIRQ和HI_SOFTIRQ兩種。
一般使用tasklet_schedule會使用TASKLET_SOFTIRQ優先級5 (include/linux/interrupt.h中檢視優先級)
使用tasklet_hi_schedule 會使用HI_SOFTIRQ優先級0
總結:軟中斷的優點:優先級比普通的程序高,排程速度快。
軟中斷的缺點:處于中斷上下文,是以不能睡眠
工作隊列:
工作隊列的執行上下文是核心線程,是以可以排程和睡眠。這就意味着需要擷取大量記憶體時、需要擷取信号量時、
阻塞IO時,工作隊列比較有效。
實作方法:
1.struct workqueue_struct test_wq; //定義工作隊列
2.實作工作隊列處理函數test_func();
3.INIT_WORK(&test_wq,test_func);//初始化工作隊列
DECLARE_WROK(name,func);//定義并初始化工作隊列
4.schedule_work(&test_wq); //排程工作隊列,成功傳回1
schedule_delayed_work(&test_wq,delay); //延遲排程
總結:
1,需要睡眠,阻塞的,隻能用工作隊列。
2,短時間内中斷數量很多的,任務隊列,軟中斷會更好。例如網絡。
3,對性能要求很高的話,軟中斷最好。
4,使用任務隊列時,應該注意同一個任務被多次調用,同一個函數被多個任務隊列注意。
5,軟中斷要注意SMP,函數的重入。
------------------------------------------------------------------------------------------------------------
| |tasklet | 軟中斷 | 工作隊列
|-----------------------------------------------------------------------------------------------------------
|SMP |向一個CPU注冊 |多個CPU并發 | 預設同一個CPU上或指定CPU
| | | |(queue_delayed_work_on)
|-----------------------------------------------------------------------------------------------------------
|上下文 |中斷 |中斷 |程序
|函數 |1.同類型隻能執行一個 |1.不能阻塞,睡眠 |1.可阻塞,睡眠
| |2.不能阻塞睡眠 |2.不帶參數 |2.不帶參數
| |3.可帶參數 |3.ksoftirqd核心線程 | 3.events/0核心線程
| |4.ksoftirqd核心線程 | |
|-----------------------------------------------------------------------------------------------------------
|執行 |1.單個tasklet串行 |1.同一個CPU上串行 |1.程序排程内,可重新排程
| |2.同一個tasklet,隻響應最後一次| 2.不同的CPU并行 |
| | |3.不能搶占另一軟中斷 |
| |3.多個之間為并行 | |
|-----------------------------------------------------------------------------------------------------------
|atomic_context |原子模式執行 |原子模式執行 |不是
|-----------------------------------------------------------------------------------------------------------
|響應時間 |1.tasklet_hi_schedule更早 |1.最遲在下一個time tick時, | 可指定延後時間,jiffies機關(1ms-百ms)
| |2.最遲在下一個time tick時, |中斷中調用,就是中斷完成 |核心線程,上下文切換
| |中斷中調用,就是中斷完成 |後立即執行 |
| |後立即執行 | |
|-----------------------------------------------------------------------------------------------------------
|開銷 |次之(與軟中斷接近) |最小 |開銷最大