新闻资讯
Group news
青岛广盛源肥业有限公司    您的位置: 首页  >  新闻资讯  >  正文

linux中断处理之IRQ中断

2019年10月12日 文章来源:网络整理 热度:148℃ 作者:刘英

在前一个专题里曾分析过所有IRQ中断处理流程,经过SAVE_ALL保存硬件环境后,都会进入do_IRQ()进行处理,今天接着分析do_IRQ()处理的相关东西.分为两部中断处理程序与软中断两个大的部份进行介绍.

二:中断处理程序

在驱动程序中,通常使用request_irq()来注册中断处理程序.我们先从注册中断处理程序的实现说起.

/*

irq:可断号

handler:中断处理程序

irqflags:中断处理标志.SA_SHIRQ:共享中断线 SA_INTERRUPT:快速处理中断

必须在关中断的情况下运行.SA_SAMPLE_RANDOM:该中断可能用于产生一个随机数

devname dev_id:设备名称与ID     

*/

int request_irq(unsigned int irq,

irqreturn_t (*handler)(int, void *, struct pt_regs *),

unsigned long irqflags,

const char * devname,

void *dev_id)

{

int retval;

struct irqaction * acTIon;

#if 1

if (irqflags & SA_SHIRQ) {

if (!dev_id)

printk("Bad boy: %s (at 0x%x) called us without a dev_id!\n", devname, (&irq)[-1]);

}

#endif

//参数有效性判断

if (irq >= NR_IRQS)

return -EINVAL;

if (!handler)

return -EINVAL;

// 分配一个irqacTIon

acTIon = (struct irqacTIon *)

kmalloc(sizeof(struct irqaction), GFP_ATOMIC);

if (!action)

return -ENOMEM;

action->handler = handler;

action->flags = irqflags;

cpus_clear(action->mask);

action->name = devname;

action->next = NULL;

action->dev_id = dev_id;

//将创建并初始化完在的action加入irq_desc[NR_IRQS]

retval = setup_irq(irq, action);

if (retval)

kfree(action);

return retval;

}

上面涉及到的irqaction结构与irq_desc[]的关系我们在上一节我们已经详细分析过了,这里不再赘述.

转进setup_irq():

int setup_irq(unsigned int irq, struct irqaction * new)

{

int shared = 0;

unsigned long flags;

struct irqaction *old, **p;

irq_desc_t *desc = irq_desc + irq;

//如果hander == no_irq_type:说明中断控制器不支持该IRQ线

if (desc->handler == &no_irq_type)

return -ENOSYS;

sif (new->flags & SA_SAMPLE_RANDOM) {

rand_initialize_irq(irq);

}

/*

* The following block of code has to be executed atomically

*/

spin_lock_irqsave(&desc->lock,flags);

p = &desc->action;

if ((old = *p) != NULL) {

//判断这条中断线上的中断处理程序是否允许SHARE

/* Can't share interrupts unless both agree to */

if (!(old->flags & new->flags & SA_SHIRQ)) {

spin_unlock_irqrestore(&desc->lock,flags);

return -EBUSY;

}

/* add new interrupt at end of irq queue */

do {

p = &old->next;

old = *p;

} while (old);

shared = 1;

}

//将其添加到中断处理函数链的末尾

*p = new;

//如果这一条线还没有被占用,初始化这条中断线

//包含清标志,在8259A上启用这条中断线

if (!shared) {

desc->depth = 0;

desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING | IRQ_INPROGRESS);

desc->handler->startup(irq);

}

spin_unlock_irqrestore(&desc->lock,flags);

//在proc下建立相关的文件

register_irq_proc(irq);

return 0;

}

现在知道怎么打一个中断处理程序挂到irq_desc[NR_IRQS]数组上了,继续分析中断处理中如何调用中断处理函数.从我们开篇时说到的do_IRQ()说起.

asmlinkage unsigned int do_IRQ(struct pt_regs regs)

{    

//屏蔽高位,取得中断号

int irq = regs.orig_eax & 0xff; /* high bits used in ret_from_ code  */

//取得中断号对应的desc结构

irq_desc_t *desc = irq_desc + irq;

struct irqaction * action;

unsigned int status;

irq_enter();

// 调试用,忽略

#ifdef CONFIG_DEBUG_STACKOVERFLOW

/* Debugging check for stack overflow: is there less than 1KB free? */

{

long esp;

__asm__ __volatile__("andl %%esp,%0" :

"=r" (esp) : "0" (THREAD_SIZE - 1));

if (unlikely(esp < (sizeof(struct thread_info) + STACK_WARN))) {

printk("do_IRQ: stack overflow: %ld\n",

esp - sizeof(struct thread_info));

dump_stack();

}

}

#endif

//更新统计计数

kstat_this_cpu.irqs[irq]++;

spin_lock(&desc->lock);

//给8259 回一个ack.回ack之后,通常中断控制会屏蔽掉此条IRQ线

desc->handler->ack(irq);

//清除IRQ_REPLAY IRQ_WAITING标志

status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);

//设置IRQ_PENDING:表示中断被应答,但没有真正被处理

status |= IRQ_PENDING; /* we _want_ to handle it */

/*

* If the IRQ is disabled for whatever reason, we cannot

* use the action we have.

*/

action = NULL;

//中断被屏蔽或者正在处理

//IRQ_DIASBLED:中断被禁用

//IRQ_INPROGRESS:这个类型的中断已经在被另一个CPU处理了

if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) {

action = desc->action;

status &= ~IRQ_PENDING; /* we commit to handling */

//置位,表示正在处理中...

status |= c; /* we are handling it */

}

desc->status = status;

//没有挂上相应的中断处理例程或者不满足条件

if (unlikely(!action))

goto out;

for (;;) {

irqreturn_t action_ret;

u32 *isp;

union irq_ctx * curctx;

union irq_ctx * irqctx;

curctx = (union irq_ctx *) current_thread_info();

irqctx = hardirq_ctx[smp_processor_id()];

spin_unlock(&desc->lock);

//通常curctx == irqctx.除非中断程序使用独立的4K堆栈.

if (curctx == irqctx)

action_ret = handle_IRQ_event(irq, ®s, action);

else {

/* build the stack frame on the IRQ stack */

isp = (u32*) ((char*)irqctx + sizeof(*irqctx));

irqctx->tinfo.task = curctx->tinfo.task;

irqctx->tinfo.real_stack = curctx->tinfo.real_stack;

irqctx->tinfo.virtual_stack = curctx->tinfo.virtual_stack;

irqctx->tinfo.previous_esp = current_stack_pointer();

*--isp = (u32) action;

*--isp = (u32) ®s;

*--isp = (u32) irq;

asm volatile(

"       xchgl   %%ebx,%%esp     \n"

"       call    handle_IRQ_event \n"

"       xchgl   %%ebx,%%esp     \n"

: "=a"(action_ret)

: "b"(isp)

: "memory", "cc", "edx", "ecx"

);

}

spin_lock(&desc->lock);

//调试用,忽略

if (!noirqdebug)

note_interrupt(irq, desc, action_ret, ®s);

if (curctx != irqctx)

irqctx->tinfo.task = NULL;

//如果没有要处理的中断了,退出

if (likely(!(desc->status & IRQ_c)))

break;

//又有中断到来了,继续处理

desc->status &= ~c;

}

//处理完了,清除IRQ_INPROGRESS标志

desc->status &= ~IRQ_INPROGRESS;

out:

/*

* The ->end() handler has to deal with interrupts which got

* disabled while the handler was running.

*/

//处理完了,调用中断控制器的end.通常此函数会使中断控制器恢复IRQ线中断

desc->handler->end(irq);

spin_unlock(&desc->lock);

//irq_exit():理论上中断处理完了,可以处理它的下半部了

irq_exit();

return 1;

}

这段代码比较简单,但里面几个标志让人觉的很迷糊,列举如下:

IRQ_DISABLED:相应的IRQ被禁用.既然中断线被禁用了,也就不会产生中断,进入do_IRQ()了?因为电子器件的各种原因可能会产生 “伪中断”上报给CPU.

IRQ_PENDING:CPU收到这个中断信号了,已经给出了应答,但并末对其进行处理.回顾上面的代码,进入do_IRQ后,发送ack,再设置此标志.

IRQ_ INPROGRESS:表示这条IRQ线的中断正在被处理.为了不弄脏CPU的高速缓存.把相同IRQ线的中断放在一起处理可以提高效率,且使中断处理程序不必重入

上一篇:你知道linux kernel内存碎片防治技术?


下一篇:需要了解的Linux的IRQ中断子系统

友情链接
Links