中断可以按照三种方式分类:
1. cpu内部中断和外部中断:
Cpu内部中断:中断源来自于cpu内部。包括软件中断指令SWI,溢出,除法错误等。操作系统从用户态切换到内核态就是借助内部中断SWI。
外部中断来自于外设,由外设提出的请求。
2. 可屏蔽中断和不可屏蔽中断。中断被屏蔽后,该中断不再得到响应。
3. 向量中断和非向量中断。采用向量中断的cpu通常为不同的中断分配不同的中断号,当检测到某中断到来后,自动跳转到对应的入口地址执行。向量中断由硬件提供中断服务程序入口地址。非向量中断由软件提供中断服务程序入口地址。
Linux中断处理的架构
中断处理机制分为顶半部(Top half 处理比较紧急的功能,往往是简单读取寄存器的中断中断和清除中断,并将底半部程序添加到底半部的执行队列中区)和底半部(bottom half 处理不太紧急的,耗时的部分)。这样的设计可以使得系统能够服务更多的中断请求。
中断编程的理论
1. 申请中断
返回0表示申请成功
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
{
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}
typedef irqreturn_t (*irq_handler_t)(int, void *);
*Dev_id must be globally unique. Normally the address of the
*device data structure is used as the cookie. Since the handler
*receives this value it makes sense to use it.
*Flags:
*
*IRQF_SHAREDInterrupt is shared
*IRQF_DISABLEDDisable local interrupts while processing
*IRQF_SAMPLE_RANDOMThe interrupt can be used for entropy
*IRQF_TRIGGER_*Specify active edge(s) or level
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id)
{
struct irqaction *action;
struct irq_desc *desc;
int retval;
//如果设定为共享中断但是dev_id为空,就不能区分该共享中断了,返回错误
if ((irqflags & IRQF_SHARED) && !dev_id)
return -EINVAL;
//检验中断号,不能超过系统能处理的最大中断号
//#define nr_irqsNR_IRQS
//#define irq_to_desc(irq)(&irq_desc[irq])
//中断注册的本质是填充irq_desc[irq]全局数组
desc = irq_to_desc(irq);
if (!desc)
return -EINVAL;
。。。
chip_bus_lock(irq, desc);
//核心是把中断处理函数添加到全局的中断描述结构体数组中
retval = __setup_irq(irq, desc, action);
chip_bus_sync_unlock(irq, desc);
}
//中断描述结构体
struct irq_desc {
unsigned intirq;
struct timer_rand_state *timer_rand_state;
unsigned int*kstat_irqs;
#ifdef CONFIG_INTR_REMAP
struct irq_2_iommu*irq_2_iommu;
#endif
irq_flow_handler_thandle_irq;
struct irq_chip*chip;
struct msi_desc*msi_desc;
void*handler_data;
void*chip_data;
struct irqaction*action;/* IRQ action list */
}
中断描述结构体的初始化在handler.c中
struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
[0 ... NR_IRQS-1] = {
.status = IRQ_DISABLED,
.chip = &no_irq_chip,
.handle_irq = handle_bad_irq,
.depth = 1,
.lock = __SPIN_LOCK_UNLOCKED(irq_desc->lock),
}
};
2. 中断释放
本质上是移除添加的中断处理函数
static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
{
struct irq_desc *desc = irq_to_desc(irq);
struct irqaction *action, **action_ptr;
unsigned long flags;
WARN(in_interrupt(), "Trying to free IRQ %d from IRQ context!\n", irq);
if (!desc)
return NULL;
。。。
/*
* There can be multiple actions per IRQ descriptor, find the right
* one based on the dev_id:
*/
action_ptr = &desc->action;
for (;;) {
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;…
}
3. 使能和屏蔽中断
屏蔽中断源
void disable_irq(unsigned int irq)
void __disable_irq(struct irq_desc *desc, unsigned int irq, bool suspend)
{
if (suspend) {
if (!desc->action || (desc->action->flags & IRQF_TIMER))
return;
desc->status |= IRQ_SUSPENDED;
}
if (!desc->depth++) {
desc->status |= IRQ_DISABLED;//设置irq_desc数组的标志位
desc->chip->disable(irq);
}
}
使能中断:
void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume)
{
if (resume)
desc->status &= ~IRQ_SUSPENDED;
switch (desc->depth) {
case 0:
err_out:
WARN(1, KERN_WARNING "Unbalanced enable for IRQ %d\n", irq);
break;
case 1: {
unsigned int status = desc->status & ~IRQ_DISABLED;//设置标志位
if (desc->status & IRQ_SUSPENDED)
goto err_out;
/* Prevent probing on this irq: */
desc->status = status | IRQ_NOPROBE;
check_irq_resend(desc, irq);
/* fall-through */
}
default:
desc->depth--;
}
}
3.底半部机制
Tasklet的使用方法
定义一个tasklet结构体并将需要做底半部处理的函数指针赋值到这个结构体中,data为传入这个函数的参数
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
tasklet结构
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);
unsigned long data;
};
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_schedule(t);
}
//产生一个软中断,软中断是硬中断服务程序对内核的中断。Tasklet依靠软中断实现
void __tasklet_schedule(struct tasklet_struct *t)
{
unsigned long flags;
local_irq_save(flags);
t->next = NULL;
*__get_cpu_var(tasklet_vec).tail = t;
__get_cpu_var(tasklet_vec).tail = &(t->next);//将该tasklet添加到tasKlet向量链表尾部
raise_softirq_irqoff(TASKLET_SOFTIRQ);
local_irq_restore(flags);
}
对软中断的响应:
static void tasklet_action(struct softirq_action *a)
{
//遍历tasklet向量链表,并对所有待处理的函数进行调用
while (list) {
struct tasklet_struct *t = list;
list = list->next;
if (tasklet_trylock(t)) {
if (!atomic_read(&t->count)) {
if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
BUG();
t->func(t->data);
tasklet_unlock(t);
continue;
}
tasklet_unlock(t);
}
local_irq_disable();
t->next = NULL;
local_irq_disable();
//把tasklet从tasklet向量链表中移除
t->next = NULL;
*__get_cpu_var(tasklet_vec).tail = t;
__get_cpu_var(tasklet_vec).tail = &(t->next);
__raise_softirq_irqoff(TASKLET_SOFTIRQ);
local_irq_enable();
}