在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_tasklet的tasklet並將其與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底半部機制的函數。