一.中斷的基本處理:
簡單的中斷處理,主要是使用request_irq()和free_irq()。
In fact, the kernel create a global array( irq_desc[] ) for manage all information about irq.Included slot-function, irq_chip and so on. By the way.Each irq is corresponding to a member of irq_desc. 在kernel建立了一個全局變量數組irq_desc[],用來記錄每個中斷的信息。
slot-function is used to record what this irq need to do when a new interrupy arrived.(many user function)
irq_chip is a structure, record many callback-function about how to control this irq.( i.e how to enable/disable irq, how to set handware status)
the following is some useful function:
/** 中斷處理函數*/
can_request_irq(unsigned int irq,unsigned long irqflags); //查詢是否可以申請
request_irq(unsigned int irq,irq_handler_t handler,unsigned long irqflags,const char * devname,void * dev_id); //申請
free_irq(unsigned int irq,void * dev_id); //釋放中斷
/** 擴展處理函數*/
disable_irq(unsigned int irq); //禁止中斷,如果有當前中斷,會等待該中斷執行完
disable_irq_nosync(unsigned int irq); //禁止中斷,會直接返回
enable_irq(unsigned int irq);
local_irq_disable();
local_irq_save(flags);
local_irq_enable();
local_irq_restore(x);
set_fiq_handler(void * start,unsigned int length);
看一下free_irq的源碼,發現對irq資源的回收就是從irq_desc[]中找到該irq對應的信息記錄變量desc。然後從desc->action的註冊函數鏈表中取出需要註銷的函數。
/*
* Internal function to unregister an irqaction - used to free
* regular and special interrupts that are part of the architecture.
*/
static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
{
struct irq_desc *desc = irq_to_desc(irq); //kernel建立了一個全局變量數組irq_desc[],用來存放所有的irq中斷信息。
struct irqaction *action, **action_ptr; //
struct task_struct *irqthread;
unsigned long flags;
WARN(in_interrupt(), "Trying to free IRQ %d from IRQ context!\n", irq);
if (!desc)
return NULL;
spin_lock_irqsave(&desc->lock, flags);
/*
* There can be multiple actions per IRQ descriptor, find the right
* one based on the dev_id:
*/
action_ptr = &desc->action;
for (;;) {//根據dev_id找到該irq對應的鏈表中的部分
action = *action_ptr;
if (!action) {
WARN(1, "Trying to free already-free IRQ %d\n", irq);
spin_unlock_irqrestore(&desc->lock, flags);
return NULL;
}
if (action->dev_id == dev_id)
break;
action_ptr = &action->next;
}
/* Found it - now remove it from the list of entries: */
*action_ptr = action->next;
/* Currently used only by UML, might disappear one day: */
#ifdef CONFIG_IRQ_RELEASE_METHOD
if (desc->chip->release)
desc->chip->release(irq, dev_id);
#endif
/* If this was the last handler, shut down the IRQ line: */
if (!desc->action) {
desc->status |= IRQ_DISABLED;
if (desc->chip->shutdown)
desc->chip->shutdown(irq);
else
desc->chip->disable(irq);
}
irqthread = action->thread;
action->thread = NULL;
spin_unlock_irqrestore(&desc->lock, flags);
unregister_handler_proc(irq, action);
/* Make sure it's not being used on another CPU: */
synchronize_irq(irq);
if (irqthread) {
if (!test_bit(IRQTF_DIED, &action->thread_flags))
kthread_stop(irqthread);
put_task_struct(irqthread);
}
#ifdef CONFIG_DEBUG_SHIRQ
/*
* It's a shared IRQ -- the driver ought to be prepared for an IRQ
* event to happen even now it's being freed, so let's make sure that
* is so by doing an extra call to the handler ....
*
* ( We do this after actually deregistering it, to make sure that a
* 'real' IRQ doesn't run in * parallel with our fake. )
*/
if (action->flags & IRQF_SHARED) {
local_irq_save(flags);
action->handler(irq, dev_id);
local_irq_restore(flags);
}
#endif
return action;
}
二.中斷處理擴展:
爲了改善中斷處理效率,中斷處理函數被分成了兩部分:頂半部和底半部。底半部有三種實現方式:tasklet,工作隊列和軟中斷。
2.1 Tasklet
tasklet運行於軟中斷上下文,所以在其處理函數中不允許睡眠。在內核有一個全局鏈表用於記錄需要完成的任務,對應的處理進程爲守護進程 ksoftirqd/0。一般在執 行中斷上半部後會執行下半部,到下半部時如果有新的中斷到達,則喚醒ksoftirqd/0來執行當前部分,而自己去處理新中斷。其函數解析如下:
/** 信息結構體*/
struct tasklet_struct
{
struct tasklet_struct *next; //信息結構體被做成鏈表
unsigned long state; //
atomic_t count; //
void (*func)(unsigned long); //回調函數
unsigned long data; //傳給回調函數的參數
};
/** 初始化*/
tasklet_init(struct tasklet_struct * t,void(* func)(unsigned long),unsigned long data);
DECLARE_TASKLET(name,func,data);
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
/** schedule*/
tasklet_schedule(a);
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_schedule(t);
}
void __tasklet_schedule(struct tasklet_struct *t)
{
unsigned long flags;
local_irq_save(flags);
t->next = NULL;
*__get_cpu_var(tasklet_vec).tail = t; //??將新task添加到全局task鏈表中
__get_cpu_var(tasklet_vec).tail = &(t->next); //
raise_softirq_irqoff(TASKLET_SOFTIRQ); //掛起TASKLET_SOFTIRQ類型的軟中斷,並根據需要喚醒守護進程
local_irq_restore(flags);
}
/*
* This function must run with irqs disabled!
*/
inline void raise_softirq_irqoff(unsigned int nr)
{
__raise_softirq_irqoff(nr); //掛起軟中斷nr
/*
* If we're in an interrupt or softirq, we're done
* (this also catches softirq-disabled code). We will
* actually run the softirq once we return from
* the irq or softirq.(假如在中斷中,該函數一定會被執行)
*
* Otherwise we wake up ksoftirqd to make sure we
* schedule the softirq soon.(否則就叫醒守護進程)
*/
if (!in_interrupt()) //假如不在中斷上下文
wakeup_softirqd(); //叫醒softirq守護進程,它會保證該函數執行一次
}
2.2 工作隊列
工作隊列是一個直接將一個任務封包掛到全局鏈表上交給一個worker process處理,所以是工作在進程上下文,允許睡眠以及進程調度操作。
//重要結構體
struct work_struct;
struct workqueue_struct;
struct cpu_workqueue_struct;
//初始化
INIT_WORK(_work,_func);
DECLARE_WORK(n,f);
//常用函數
schedule_work(a); --將新工作a添加到一個全局的工作隊列中,一般由守護進程events複製處理;但如果需要可以創建自定義 的worker process。
2.3 軟中斷
處理器的所有狀態中有一種是軟中斷狀態,這是一種可被打斷的中斷狀態(其他中斷是不允許被打斷的)。 one of most important feasure, for softirq, is interruptible.this is very important for ensure the system quickly answer a new interrupt ,because the rest of all interrupt can't be interrupt.
爲了方便事物分類處理又在內部進行了邏輯分類,
for example, tasklet is corresponding to TASKLET_SOFTIRQ. we often raise the softirq( the
TASKLET_SOFTIRQ type) when a new tasklet arrived.
the type of softirq is
enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
TASKLET_SOFTIRQ, // this is a tasklet thing
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */
NR_SOFTIRQS
};
In the next is some common function:
//重要結構體
struct softirq_action;
struct softirq_action
{
void (*action)(struct softirq_action *);
};
//常用函數
open_softirq(int nr,void(* action)(struct softirq_action *)); //reigister a function for a softirq( 註冊軟中斷對應的處理函數)
raise_softirq(unsigned int nr); //raise a softirq(觸發一個軟中斷)
local_bh_disable(void); //禁止軟中斷,tasklet底半部機制
local_bh_enable(void); //
一個非常棒的關於軟中斷的說明在這裏,:
void open_softirq(int nr, void (*action)(struct softirq_action *))
{
softirq_vec[nr].action = action;
}