中斷的概念:是指在CPU執行過程中,遇到壹些突發事件需要緊急處理,CPU暫停當前程序的執行,轉而處理突發事件。
處理完成後,CPU返回原程序中斷的位置繼續執行。
中斷的分類:內部中斷和外部中斷。
內部中斷:中斷源來自CPU(軟件中斷指令、溢出、觸發錯誤等。)
外部中斷:中斷源來自CPU外部,由外設請求。
屏蔽中斷和非屏蔽中斷:
可屏蔽中斷:它可以被屏蔽字屏蔽。屏蔽後,中斷將不再接收響應。
非平布中斷:無法屏蔽。
向量中斷和非向量中斷:
向量中斷:CPU通常給不同的中斷分配不同的中斷號,當檢測到壹個中斷號到來時,自動跳轉到該中斷號對應的地址執行。
非向量中斷:多個中斷* * *共享壹個入口地址。輸入入口地址後,軟件判斷中斷標誌以識別哪個是中斷。
也就是說,軟件為向量中斷提供中斷服務程序的入口地址,軟件為非向量中斷提供入口地址。
/*典型的非向量中斷首先判斷中斷源,然後調用不同中斷源的中斷處理程序*/
irq_handler()
{
...
int int _ src = read _ int _ status();/*讀取硬件的中斷相關寄存器*/
Switch(int_src){//判斷中斷標誌
案例開發_A:
dev _ a _ handler();
打破;
案例開發_B:
dev _ b _ handler();
打破;
...
默認值:
打破;
}
...
}
定時器中斷原理:
定時器也在硬件中中斷,PIT(可編程間隔定時器)接收時鐘輸入。
當時鐘脈沖到達時,將當前計數值增加1,並與設定計數值進行比較。如果相等,則證明計數周期已滿,並產生壹個定時器中斷。
重置計數值。
如下圖所示:
Linux中斷處理程序架構:
Linux把中斷分為:上半部分和下半部分。
頂板:完成盡可能少的緊急功能,通常只是讀取寄存器中的中斷狀態並清除中斷標誌。
“註冊中斷”的工作(即將下半部分執行隊列中的下半部分處理程序掛在設備上)
特點:反應快。
下半部分:中斷處理的大部分工作都在下半部分,它幾乎完成了中斷處理程序的所有工作。
特點:處理相對不太緊急的事件。
提示:查看Linux中的/proc/interrupts文件,獲取系統中斷的統計信息。
如下圖所示:
第壹列是中斷號,第二列是CPU產生中斷的次數。
介紹完相關的基本概念,我們壹起來討論壹下Linux中斷編程。
Linux中斷編程:
1.請求和釋放中斷
中斷請求:
int request_irq(無符號int irq,irq_handler_t handler,
無符號長整型irqflags,const char *devname,void *dev_id)
參數描述:irq是要應用的硬件中斷號。
Handler是壹個向系統註冊的中斷處理程序(上半部分),它是壹個回調函數。當中斷發生時,系統調用它並將
Dev_id參數傳遞給它。
Irqflags:它是中斷處理的壹個屬性,可以指定如何觸發和處理中斷:
觸發模式:IRQF_TRIGGER_RISING、IRQF_TRIGGER_FALLING、IRQF_TRIGGER_HIGH、IRQF_TRIGGER_LOW。
處理模式:IRQF_DISABLE表示中斷處理程序是壹個快速處理程序,當快速處理程序被調用時,所有中斷都被屏蔽。
IRQF_SHARED表示多個設備* * *享受中斷,dev_id在中斷* * *享受時使用,壹般設置為NULL。
返回值:0表示成功,-EINVAL表示中斷號無效,-EBUSY表示中斷已被占用,無法享用。
上半部分處理程序的類型irq_handler_t定義為
typedef IRQ return _ t(* IRQ _ handler _ t)(int,void *);
typedef int IRQ return _ t;
釋放IRQ
當然,有求必有放。
void free_irq(無符號int irq,void * dev _ id);
參數定義類似於request_irq。
3.啟用和屏蔽中斷
void disable _ IRQ(int IRQ);//等待當前中斷處理完成(最好不要用在頂板上,妳懂的)
void disable _ IRQ _ no sync(int IRQ);//立即返回
void enable _ IRQ(int IRQ);//
4.屏蔽該CPU中的所有中斷:
#定義local _ IRQ _ save(標誌)...//中斷禁用並保存狀態。
void local _ IRQ _ disable(void);//中斷禁用,不保存狀態。
下面分別介紹壹下上半部分和下半部分的實現機制。
下半部分機制:
簡介:下半部分機制主要包括小任務、工作隊列和軟中斷。
1.下半部分是思考的方式之壹。
(1)我們需要定義微線程機器處理器並關聯它們。
例如:
void my_tasklet_func(無符號長整型);/*定義處理程序*/
DECLARE_TASKLET(my_tasklet,my_tasklet_func,data);
/*上面的代碼定義了壹個名為my_tasklet的小任務,並設置了其余的。
My_tasklet_func()函數綁定,傳入的參數是data*/
(2)時間安排
tasklet_schedule。my _ tasklet);
//使用此函數調度操作。
Tasklet使用模板:
/*定義小任務和下半部分函數,並將它們關聯*/
void xxx_do_tasklet(無符號長整型);
DECLARE_TASKLET(xxx_tasklet,xxx_do_tasklet,0);
/*中斷處理的下半部分*/
void xxx_do_tasklet(無符號長整型)
{
...
}
/*中斷處理上半部分*/
IRQ return _ t XXX _ interrupt(int IRQ,void *dev_id)
{
...
tasklet_schedule。XXX _ tasklet);//調度樓層部門
...
}
/*設備驅動模塊加載函數*/
int __init xxx_init(void)
{
...
/*請求中斷*/
結果=請求_irq(xxx_irq,XXX _中斷,
IRQF_DISABLED,“xxx”,NULL);
...
返回IRQ _ HANDLED
}
/*設備驅動模塊卸載功能*/
void __exit xxx_exit(void)
{
...
/*釋放中斷*/
free_irq(xxx_irq,XXX _ interrupt);
...
}
2.底部半工作隊列的第二種實現方法
使用方法類似於tasklet。
相關操作:
結構work _ struct my _ wq/*定義工作隊列*/
void my_wq_func(無符號長整型);/*定義處理程序*/
INIT_WORK()可用於初始化工作隊列,並將工作隊列綁定到處理程序。
初始化工作(& ampmy_wq,(void (*)(void *))my_wq_func,NULL);
/*初始化工作隊列並將其綁定到處理程序*/
schedule _ work(& amp;my _ wq);/*調度工作隊列執行*/
/*工作隊列使用模板*/
/*定義工作隊列和相關功能*/
struct work_struct(無符號長整型);
void xxx_do_work(無符號長整型);
/*中斷處理的下半部分*/
void xxx_do_work(無符號長整型)
{
...
}
/*中斷處理上半部分*/
/*中斷處理上半部分*/
IRQ return _ t XXX _ interrupt(int IRQ,void *dev_id)
{
...
schedule _ work(& amp;my _ wq);//調度下半部分
...
返回IRQ _ HANDLED
}
/*設備驅動模塊加載函數*/
int xxx_init(void)
{
...
/*請求中斷*/
結果=請求_irq(xxx_irq,XXX _中斷,
IRQF_DISABLED,“xxx”,NULL);
...
/*初始化工作隊列*/
初始化工作(& ampmy_wq,(void (*)(void *))xxx_do_work,NULL);
}
/*設備驅動模塊卸載功能*/
void xxx_exit(void)
{
...
/*釋放中斷*/
free_irq(xxx_irq,XXX _ interrupt);
...
}