中断可以按照三种方式分类:

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

//tasklettasklet向量链表中移除

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

}