linux中断处理原理机制

本笔记摘自《深入linux设备驱动程序内核机制》

1、硬件中断框架


其中PIC为中断控制器,现在的ARM SOC处理器已经集成了PIC

irq是发生中断后,从中断寄存器中读出的中断号码,用于识别是发生了哪个中断

INT代表触发CUP的统一外部中断中断信号,进过一些汇编代码处理后,调用统一的外部中断处理函数asm_do_IRQ

2、中断软件处理流程


2.1、重要数据结构irq_desc irqaction

struct irq_desc {
    unsigned int        irq;
    struct irq_data		irq_data;
    irq_flow_handler_t    handle_irq;
    
    struct irqaction    *action;    /* IRQ action list */
   
} ____cacheline_internodealigned_in_smp;

struct irqaction {
    irq_handler_t handler;
  
    struct irqaction *next;
    int irq;
   
};

1、发生中断时,内核会根据中断号irq查询irq_desc表,

2、然后从中irqaction里面调用驱动程序用irq_request()函数注册到系统的具体的设备中断处理函数action->hendler

2.2、中断的注册irq_request

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

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;

	/*
	 * Sanity-check: shared interrupts must pass in a real dev-ID,
	 * otherwise we'll have trouble later trying to figure out
	 * which interrupt is which (messes up the interrupt freeing
	 * logic etc).
	 */
	if ((irqflags & IRQF_SHARED) && !dev_id)
		return -EINVAL;

	desc = irq_to_desc(irq);
	if (!desc)
		return -EINVAL;

	if (!irq_settings_can_request(desc) ||
	    WARN_ON(irq_settings_is_per_cpu_devid(desc)))
		return -EINVAL;

	if (!handler) {
		if (!thread_fn)
			return -EINVAL;
		handler = irq_default_primary_handler;
	}

	action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
	if (!action)
		return -ENOMEM;

	action->handler = handler;     //填充中断处理函数
	action->thread_fn = thread_fn;
	action->flags = irqflags;
	action->name = devname;
	action->dev_id = dev_id;

	chip_bus_lock(desc);
	retval = __setup_irq(irq, desc, action); //将两个结构体成员关联起来,注册到内核系统中
	chip_bus_sync_unlock(desc);

	if (retval)
		kfree(action);

#ifdef CONFIG_DEBUG_SHIRQ_FIXME
	if (!retval && (irqflags & IRQF_SHARED)) {
		/*
		 * It's a shared IRQ -- the driver ought to be prepared for it
		 * to happen immediately, so let's make sure....
		 * We disable the irq to make sure that a 'real' IRQ doesn't
		 * run in parallel with our fake.
		 */
		unsigned long flags;

		disable_irq(irq);
		local_irq_save(flags);

		handler(irq, dev_id);

		local_irq_restore(flags);
		enable_irq(irq);
	}
#endif
	return retval;
}

2.3、中断顶半部和底半部

asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
	handle_IRQ(irq, regs);
}



void handle_IRQ(unsigned int irq, struct pt_regs *regs)

{
	struct pt_regs *old_regs = set_irq_regs(regs);

	irq_enter();

	/*
	 * Some hardware gives randomly wrong interrupts.  Rather
	 * than crashing, do something sensible.
	 */
	if (unlikely(irq >= nr_irqs)) {
		if (printk_ratelimit())
			printk(KERN_WARNING "Bad IRQ%u\n", irq);
		ack_bad_irq(irq);
	} else {
		generic_handle_irq(irq);
	}

	/* AT91 specific workaround */
	irq_finish(irq);

	irq_exit();
	set_irq_regs(old_regs);
}

1、顶半部在irq_enter();和irq_exit();之间调用generic_handle_irq(irq);完成,此时关闭了中断,因此处理时间应该尽可能短,否则因为时间过长导致其他中断得不到相应,严重可导致内核崩溃

2、底半部在irq_exit();函数中一直不断调用函数最终调用到__do_softirq函数,此时中断时开启的,因此处理时间可以较长

3、总结:在处理中断时,可以把较短的紧急事情放到中断上半部处理,而不紧急的事情可以放到底半部处理

猜你喜欢

转载自blog.csdn.net/shenlong1356/article/details/105847525