Linux內核中斷處理機制


<什么是中斷>
計算停下當前處理任務,並保存現場,轉而去處理其他是任務,當完成任務后再回到原來的任務中去。
<中斷的分類>
a:軟中斷
    軟中斷時執行中斷指令產生的,軟中斷不用施加中斷請求信號,因此中斷的產生的不是隨機的而是由程序安排的。內核線程是實現軟中斷的助手。
b:硬中斷
    硬中斷時由外部硬件產生的,具有隨機性。
 
<中斷的實現>
int request_irq(unsigned int irq,irq_handler_t handler,unsigned long intflags,const char devname, void *dev_id)
解釋:
irq:申請的中斷號。
handler:中斷處理函數指針
irqflags:中斷處理屬性,與上半段和下半段有關系
devname:中斷設備名字
dev_id:與共享中斷號有關系。
注:該函數的主要作用是在內核中的一個重要的結構體"irq_desc"中注冊中斷號與對應的中斷處理函數。
 
<釋放中斷線>
void free_irq(unsigned int irq ,void dev_id);
注:一般操作硬件的端口都會調用ioremap()函數,該函數用來將計算實際的物理地址映射成虛擬地址,這樣系統才能訪問。
 
<中斷處理機制之上半部分和下半部>
a:Linux中中斷處理是一種很霸道的東西,只要沒有屏蔽中斷,CPU就會立即相應。為了加開處理的數據,Linux中通常將 中斷處理中的硬件相關的操作放在上半部分,耗時的操作放在下半部分——linux 內核一般將中斷處理分為兩不份:上半部(top_half)和下半部(bottom_half)。
b:上半部分
    一般調用中斷處理函數,一進入中斷處理函數就是進行相應的硬件操作,這些都是放在上半部分。然后將耗時的操作放在下半部分中。
c:下半部分
Linux中實現下半部分的處理有:softirq機制,tasklist機制(小任務片段),workqueue機制
----tasklet將任務延遲到安全時間執行,每個tasklet都和一個函數相關聯,當tasklet運行時,
----該函數就被調用,並且tasklet可以調度自己。
d:Tasklet的實現
(1)定義一個處理函數:
void tasklet_func(unsigned  long);
定義一個tasklet結構my_tasklet與tasklet_func(data)函數關聯
struct tasklet_struct  my_tasklet;
DECLARE_TASKLET(my_tasklet,tasklet_func,data);
調度tasklet
tasklet_schedule(&my_tasklet);
e:工作隊列和下半部處理
工作隊列使用方法和tasklet非常相似
(1)定義一個工作隊列:
(2)struct work_struct  my_wq;
定義一個處理函數
void my_wq_func(struct work_struct  *work);
(3)初始化工作隊列並將其處理函數綁定
INIT_WORK(&my_wq,my_wq_func);
(4)調度工作隊列執
schedule_work(&my_wq);//該函數將其提交給內核默認工作者隊列線程
queue_work();//該函數會將其提交給專用的工作者線程(可以是自己定義的)
f:Tasklet和工作隊列的區別
(1)tasklet工作在中斷上下文
(2)工作隊列工作在進程上下文
(3)tasklet處理函數中不能睡眠
(4)工作隊列中允許有睡眠
 
注:軟中斷和tasklet
(1)軟中斷定義
struct softirq_action{
Void(*action)(struct softirq_action *)
並且當前內核中的軟中斷總數固定為32個,由數組Static struct softirq_action softirq_vec[NR_SOFTIRQS]來表示。
目前只用到了其中的9個。包括定時器、網絡、tasklet等。一旦有軟中斷產生就會查詢soft_vec[]看查看哪個軟中斷被掛起了。然后執行:
h=softirq_vec;
h->action(h) ;//執行對應的軟中斷action函數。
(2)軟中斷的使用
open_softirq()掛起軟中斷處理函數:
open_softirq(TASKLET_SOFTIRQ,tasklet_action);//tasklet_action位軟中斷處理函數
(3)觸發軟中斷
rase_softirq(TASKLET_SOTFIRQ);//處理器就會在適當的時候執行軟中斷處理函數tasklet_action
總結:tasklet(小任務)是對軟中斷的一種個封裝。
 

linux 驅動中的中斷處理程序:

當發生中斷的時候,無論是裸機程序還是Linux系統都會有一個統一的入口.

裸機中的中斷入口是代碼:ldr pc,_irq;

linux 系統中的統一入口:irq_svc

 

Linux系統注冊中斷處理程序想:

requst_irq(unsigned int irq ,void(*handler)(int ,void *,struct pt_regs*),unsigned long flags,const char *devname ,void *dev_id )

 

參數分析:

unsigned long flags:參數還是一個宏,用於決定是快速中斷還是慢速中斷,或者表明該中斷是多少個設備共享。

unsigned int irq:

 

Linux系統注銷中斷處理程序:

void free_irq(unsigned int irq ,void *dev_id)

 

參數分析:

int irq:中斷號,注意這里的中斷號和裸機中的中斷號有一定的不同,因為系統預留了16個的軟中斷號,所以硬件中斷號需要加上16,系統通過中斷號找到相應的描述符表,在描述符表中找到相應個處理函數。

 

dev_id:對於共享中斷號的設備,需要提供相應的中斷號才能准確的注銷掉。

 

中斷嵌套:

為了解決中斷處理速度,Linux中將硬件處理函數和非硬件處理函數進行了分開,將非硬件操作放到工作隊列中。

 

注意:工作隊列中的結構體數組名都是使用一個struct 在后面

定義和描述中斷隊列:

struct workqueue_struct {

struct cpu_workqueue_struct *cpu_wq;

struct list_head list;

const char *name; /*workqueue name*/

int singlethread;

int freezeable; /* Freeze threads during suspend */

int rt;

}

 

定義和描述一個工作:

struct work_struct {

atomic_long_t data;

struct list_head entry;

work_func_t func;

};

 

創建一個工作隊列:
srtuct workqueue_struct * =create_workqueue(“workqueue_name”)

 

初始化工作:

INIT_WORK(struct work_struct *,func)

注意:創建工作實質是將創建的工作和相應的操作函數關聯起來

 

Linux系統提交工作

queue_work(struct workqueue_struct , func)

 

 

在大多數情況下,並把需要定義工作隊列,Linux內核中已經有一個默認的工作隊列keventd_wq,所以只需要創建工作,並初始化工作。

 

提交默認工作隊列:

schedule_work()

 

 

按鍵去抖之內核定時器:

 

定義並描述定時器:

struct timer_list {

struct list_head entry;

unsigned long expires;

void (*function)(unsigned long);

unsigned long data;

struct tvec_base *base;

};

 

初始化定時器:

init_timer(struct list_timer * keytimer)

keytimer.function = key_timerfunc()

 

找一個地方定義timerfunc()

 

Linux注冊定時器:

add_timer(struct timer_list *)

 

啟動定時器:

mod_timer(struct list_timer * ,jiffes+Hz/2)

 

 

阻塞型驅動:

背景:

當計算機驅動訪問一個硬件的時候發現訪問條件不滿足,稱之為阻塞,這時就需要將該驅動放入到等待隊列中。

 

定義等待隊列:

wait_queue_head_t  my_queue

初始化等待隊列:  

init_waitqueue_head( wai_queue_head_t* )

 

備注:定義並初始化等待隊列

DECLARE_WAIT_QUEUE_HEAD(wait_queue_head_t  my_queue)

 

進入等待隊列:

wait_event(queue,condition)  //TASK_UNINTERRUPT 模式

 

wait_event_interrupt(queue,condition)//TASK_INTERRUPT  模式

 

 

int wait_event_killable(queue,condition) //TASK_KILLABLE 模式

 

函數分析:

以上兩個函數,在條件滿足時,直接返回,並繼續執行相應的下面的程序,當條件不滿足時則掛載到等待隊列中,知道被喚醒。

 

喚醒等待隊列:

wake_up(wait_queue_t *q)  //從等待隊列中喚醒,所有TASK_UNINTERRUPT ,TASK_INTERRUPT ,TASK_KILLABLE 狀態的所有進程。

 

wake_up_intterruptible(wait_queue_t *q)//從等待隊列q中喚醒狀態僅為INTERRUPTIBLE 狀態的隊列。

 

 

 

 

設備驅動在內存中開辟內存:

kmalloc()

注意:該函數返回的是物理地址

 

釋放內存:

kfree()


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM