linux中斷介紹(概念、頂半部和底半部、linux中斷程式設計)
中斷的概念:
指CPU在執行過程中,出現某些突發事件急待處理,CPU暫停執行當前程式,轉去處理突發事件,處理完後CPU又返回原程式被中斷的位置繼續執行.
中斷的分類:
內部中斷:來自cpu內部(軟體中斷、cpu溢位、觸發錯誤等)
外部中斷:來自cpu外部,由外設觸發
遮蔽中斷和不可遮蔽中斷
可遮蔽中斷:可以通過遮蔽字被遮蔽,遮蔽後該中斷不再觸發響應
不可遮蔽中斷:中斷無法被遮蔽
向量中斷和非向量中斷
向量中斷:cpu通常為不同中斷分配不同中斷號,當檢測到某中斷號的中斷到來後,就自動跳轉到與該中斷好對應的地址執行
非向量中斷:多箇中斷共享一個向量地址,進入該入口地址後再通過判斷中斷標誌識別是哪個中斷
也就是說向量中斷由軟體提供中斷服務入口地址,非向量中斷由軟體提供中斷入口地址
定時器中斷原理
定時器再硬體上也可以用作中斷,定時器接收一個時鐘輸入,當時鍾脈衝來時,當前計數加1,並和預先設定的計數比較,如果相等,證明計數週期滿,產生定時器中斷,並復位計數值
linux中斷處理程式架構
linux將中斷分為:頂半部和底半部
頂半部:完成儘可能少的比較緊急的任務,它往往只是簡單的讀取暫存器中的中斷狀態並清除中斷標誌後就進行
”登記中斷“(也就是將底半部處理程式掛到裝置的底半部執行佇列中)的工作
特點:響應速度快
底半部:中斷處理的大部分工作都在底半部,它幾乎做了中斷處理程式的所有事情、
特點:處理相對不是非常緊急的事情
中斷基礎概念介紹完了,接下來介紹linux中斷程式設計
linux中斷程式設計
1、申請中斷
/* irq:要申請的中斷號 handler:向系統登記的中斷處理程式(頂半部),是一個回撥函式,中斷髮生時,系統呼叫它,將dev_id引數傳遞 給它 irqflags:中斷處理的屬性,可以指定中斷的觸發方式和處理方式 觸發方式:IRQF_TRIGGER_RISING、IRQF_TRIGGER_FALLING、 IRQF_TRIGGER_HIGH、IRQF_TRIGGER_LOW 處理方式:IRQF_DISABLE表明中斷處理程式是快速處理程式,快速處理程式被呼叫時遮蔽所有中斷 IRQF_SHARED表示多個裝置共享中斷 devname:設定中斷名稱,通常是裝置驅動程式的名稱 在cat /proc/interrupts中可以看到此名稱 dev_id:中斷共享時會用到,一般設定為NULL 返回值: 為0表示成功,返回-EINVAL表示中斷號無效,返回-EBUSY表示中斷已經被佔用,且不能共享 */ int request_irq(unsigned int irq,irq_handler_t handler, unsigned long irqflags,const char *devname,void *dev_id)
2、釋放中斷
void free_irq(unsigned int irq,void *dev_id);
3、使能和遮蔽中斷
void disable_irq(int irq); //等待目前中斷處理完成(最好別在頂板部使用,你懂得)
void disable_irq_nosync(int irq); //立即返回
void enable_irq(int irq);//
4、遮蔽本CPU內所有中斷
#define local_irq_save(flags) //禁止中斷並儲存狀態
void local_irq_disable(void); //禁止中斷,不儲存狀態
下面來分別介紹一下頂半部和底半部的實現機制
底半部機制:底半部機制主要有tasklet、工作佇列和軟中斷
一、底半部實現方法之1---tasklet
1、我們需要定義tasklet機器處理器並將兩者關聯
例如:
void my_tasklet_func(unsigned long);/*定義一個處理函式*/
DECLARE_TASKLET(my_tasklet,my_tasklet_func,data);
/*上述程式碼定義了名為my_tasklet的tasklet並將其餘
my_tasklet_func()函式繫結,傳入的引數為data*/
2、排程
tasklet_schedule(&my_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, IRQF_DISABLED,"xxx",NULL);
...
return IRQ_HANDLED;
}
/*裝置驅動模組解除安裝函式*/
void __exit xxx_exit(void)
{
...
/*釋放中斷*/
free_irq(xxx_irq,xxx_interrupt);
...
}
二、底半部實現方法之2---工作佇列
使用方法和tasklet類似
相關操作:
struct work_struct my_wq; /*定義一個工作佇列*/
void my_wq_func(unsigned long);/*定義一個處理函式*/
通過INIT_WORK()可以初始化這個工作佇列並將工作佇列與處理函式繫結
INIT_WORK(&my_wq,(void (*)(void *))my_wq_func,NULL);
/*初始化工作佇列並將其與處理函式繫結*/
schedule_work(&my_wq);/*排程工作佇列執行*/
/*工作佇列使用模板*/
/*定義工作佇列和關聯函式*/
struct work_struct(unsigned long);
void xxx_do_work(unsigned long);
/*中斷處理底半部*/
void xxx_do_work(unsigned long)
{
...
}
/*中斷處理頂半部*/
irqreturn_t xxx_interrupt(int irq,void *dev_id)
{
...
schedule_work(&my_wq); //排程底半部
...
return IRQ_HANDLED;
}
/*裝置驅動模組載入函式*/
int xxx_init(void)
{
...
/*申請中斷*/
result = request_irq(xxx_irq, xxx_interrupt, IRQF_DISABLED,"xxx",NULL);
...
/*初始化工作佇列*/
INIT_WORK(&my_wq,(void (*)(void *))xxx_do_work,NULL);
}
/*裝置驅動模組解除安裝函式*/
void xxx_exit(void)
{
...
/*釋放中斷*/
free_irq(xxx_irq,xxx_interrupt);
...
}
二、底半部實現方法之2---工作佇列
。。。
中斷共享
中斷共享是指多個裝置共享一根中斷線的情況
中斷共享的使用方法:
(1).在申請中斷時,使用IRQF_SHARED標識
(2).在中斷到來時,會遍歷共享此中斷的所有中斷處理程式,直到某一個函式返回
IRQ_HANDLED,在中斷處理程式頂半部中,應迅速根據硬體暫存器中的資訊參照dev_id引數,判斷是否為本裝置的中斷,若不是立即返回IR1_NONE
/*共享中斷程式設計模板*/
/*中斷處理頂半部*/
irqreturn_t xxx_interrupt(int irq,void *dev_id,struct pt_regs *regs)
{
...
int status = read_int_status();/*獲知中斷源*/
if(!is_myint(dev_id,status))/*判斷是否為本裝置中斷*/
return IRQ_NONE;/*不是本裝置中斷,立即返回*/
/*是本裝置中斷,進行處理*/
...
return IRQ_HANDLED;/*返回IRQ_HANDLER表明中斷已經被處理*/
}
/*裝置模組載入函式*/
int xxx_init(void)
{
...
/*申請共享中斷*/
result = request_irq(sh_irq, xxx_interrupt, IRQF_SHARE,"xxx", xxx_dev);
...
}
/*裝置驅動模組解除安裝函式*/
void xxx_exit()
{
...
/*釋放中斷*/
free_irq(xxx_irq,xxx_interrupt);
...
}