linux中斷機制分析

在linux設備驅動中,使用中斷的設備需要申請和釋放對應的中斷,分別使用內核提供的request()和free()函數。

1 申請IRQ

int request_irq(unsigned int irq, void (*handler)(int irq, void *dev_id ...),

                       unsigned long irqflags, const char *devname, void *dev_id);

其中

irq:要申請的硬件中斷號

irqflags是要處理的中斷屬性:

IRQF_SHARED:就是舊時代的SA_SHIRQ,共享中斷號

IRQF_DISABLED:就是就時代的SA_INTERRUPT,設置了該標誌,則執行ISR時關本地中斷

IRQF_SAMPLE_RANDON:告訴內核,本中斷源可以用作隨機數發生器的熵池

dev_id:在中斷共享時會用到,一般設置這個設備的設備結構體或者NULL

request_irq()返回0表示成功,返回-INVAL表示中斷號無效或處理函數指針爲NULL,返回-EBUSY表示中斷已經被佔用且不能共享。

2 釋放IRQ

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);

disable_irq與disable_irq_nosync的區別在於前者等待到irq的處理已經完成才返回,後者返回前並不保證irq的處理函數已經完成

這3個函數作用於可編程控制器,因此對所有的CPU都生效。

屏蔽本CPU的所有中斷:

void local_irq_disable(void);

void local_irq_save(unsigned long flags);

恢復對應中斷:

void local_irq_enable(void);

void local_irq_restore(unsigned long flags);

 

底半部機制

linux系統中實現底半部機制主要有:tasklet,工作隊列和軟中斷。其中軟中斷和tasklet運行於中斷上下文,而工作隊列則運行於進程上下文。因此,軟中斷和tasklet處理函數中不能睡眠,而工作隊列處理函數中允許睡眠”。

tasklet:

定義tasklet及其處理函數,並將兩者關聯:

void my_tasklet_func(unsigned long);

DECLARE_TASKLET(my_tasklet,my_tasklet_func,data);DECLARE_TASKLET定義名稱爲my_tasklettasklet並將其與my_tasklet_func函數綁定,傳入這個函數的參數爲data。在需要調度tasklet的時候引用tasklet_schedule函數就能使系統在適當時候運行:tasklet_schedule(&my_tasklet);

工作隊列:

struct work_struct my_wq;//定義一個工作隊列

void my_wq_func(struct work_struct * work);//定義一個處理函數

1) INIT_WORK(_work, _func, _data)

初始化指定工作,目的是把用戶指定的函數_func及_func需要的參數_data賦給work_struct的func及data變量

2) int schedule_work(struct work_struct *work)

對工作進行調度,即把給定工作的處理函數提交給缺省的工作隊列和工作者線程。工作者線程本質上是一個普通的內核線程,在默認情況下,每個CPU均有一個類型爲“events”的工作者線程,當調用schedule_work時,這個工作者線程會被喚醒去執行工作鏈表上的所有工作。

3) int schedule_delayed_work(struct work_struct *work, unsigned long delay)

延遲執行工作,與schedule_work類似。

4) void flush_scheduled_work(void)

刷新缺省工作隊列。此函數會一直等待,直到隊列中的所有工作都被執行。

5) int cancel_delayed_work(struct work_struct *work)

flush_scheduled_work並不取消任何延遲執行的工作,因此,如果要取消延遲工作,應該調用cancel_delayed_work。


以上均是採用缺省工作者線程來實現工作隊列,其優點是簡單易用,缺點是如果缺省工作隊列負載太重,執行效率會很低,這就需要我們創建自己的工作者線程和工作隊列。

1) struct workqueue_struct *create_workqueue(const char *name)

創建新的工作隊列和相應的工作者線程,name用於該內核線程的命名。

2) int queue_work(struct workqueue_struct *wq, struct work_struct *work)

類似於schedule_work,區別在於queue_work把給定工作提交給創建的工作隊列wq而不是缺省隊列。

3) int queue_delayed_work(struct workqueue_struct *wq, struct work_struct *work, unsigned long delay)

延遲執行工作。

4) void flush_workqueue(struct workqueue_struct *wq)

刷新指定工作隊列。

5) void destroy_workqueue(struct workqueue_struct *wq)

釋放創建的工作隊列。


下面一段代碼可以看作一個簡單的實作:

void my_func(void *data)

{

    char *name = (char *)data;

    printk(KERN_INFO “Hello world, my name is %s!/n”, name);

}


struct workqueue_struct *my_wq = create_workqueue(“my_wq”);

struct work_struct my_work;


INIT_WORK(&my_work, my_func, “Jack”);

queue_work(my_wq, &my_work);


destroy_workqueue(my_wq);

 

新版本中,定義了一個新的結構delayed_work用於處理延遲執行:

struct delayed_work {

    struct work_struct work;

    struct timer_list timer;

};


下面把API羅列一下,每個函數的解釋可參考之前版本的介紹或者之後的實作:

1) INIT_WORK(struct work_struct *work, work_func_t func)

2) INIT_DELAYED_WORK(struct delayed_work *work, work_func_t func)

3) int schedule_work(struct work_struct *work)

4) int schedule_delayed_work(struct delayed_work *work, unsigned long delay)

5) struct workqueue_struct *create_workqueue(const char *name)

6) int queue_work(struct workqueue_struct *wq, struct work_struct *work)

7) int queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay)

8) void flush_scheduled_work(void)

9) void flush_workqueue(struct workqueue_struct *wq)

10) int cancel_delayed_work(struct delayed_work *work)

11) void destroy_workqueue(struct workqueue_struct *wq)

 

軟中斷

在linux內核中,用softirq_action結構體表徵一個軟中斷,這個結構體中包含軟中斷處理函數指針和傳遞給該函數的參數,使用open_softirq註冊軟中斷對應的中斷函數,raise_softirq函數出發一個軟中斷。

local_bh_disable和local_bh_enable是內核中用於禁止和使能軟中斷和tasklet底半部機制的函數。

發佈了30 篇原創文章 · 獲贊 5 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章