中断分为硬件中断和软件中断,硬件中断是由于外部条件出发后导致CPU的SWI寄存器发送变化后出发的中断,软件中断是由于软件中某一个时间满足时出发的中断,所以在有限的硬件资源中,硬件中断可以说是非常宝贵的,在普通的MCU的rtos或者baremachine中,对这种中断只能一组中断使用一个IO,这样不太便以硬件的灵活性,所以在linux kernel中,引入了共享中断的方式(但是很多的自定义的RTOS项目中,程序员也借鉴了linux的方式实现了共享中断)。 共享中断: 寄存器SWI触发后,会触发软件注册的中断回调函数,我们在回调函数中依次读取属于同一组中断线上的所有IO的状态与软件上设计的是否一致,如果一致则执行这一个IO所注册的回调,这就是共享中断的实现原理。
内核中有提供了向内核注册一个中断的函数、 int request_irq(unsigned int irq,irqreturn_t (*handler)(int void *,struct pt_regs *),unsigned long flags,const char *dev_name,void *dev_id); void free_irq(unsigned int irq, void *dev_id); 返回值:成功返回0 失败则返回一个错误码。 irq表示中断号。
flags 标志设置中断触发条件的mask。 SA_INTERRUPT、当置位了, 这表示一个"快速"中断处理. 快速处理在当前处理器上禁止中断来执行 (这个主题在"快速和慢速处理"一节涉及). SA_SHIRQ、这个位表示中断可以在设备间共享. 共享的概念在"中断共享"一节中略述 SA_SAMPLE_RANDOM、表示真正随机的时间产生中断
irqreturn_t (*handler)(int void *,struct pt_regs *) 为注册的中断处理函数。 dev_name 注册的设备名。 dev_id 传递到回调函数中的参数。
当注册一个irq成功后,在文件系统的cat /proc/interrupts 可以得到该中断发生的次数,这对于驱动工程师而言是非常方便的。
中断要求快速响应,所以在中断发生时,避免进行大量运算,处理等消耗CPU时间的动作,所以linux在中断处理上进行拆分,分为上下半部 1、上半部 主要做一些简短,必要性的工作,然后唤醒等待队列(wake_up_interruptible()),唤醒任务schedule_work())等。 2、下半部主要做中断处理的一些比较繁重的计算,硬件操作等。
当SWI被触发时候,CPU的PC将会指向异常向量表(vector_irq),然后去支持响应的中断处理、 入口地址: asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
asmlinkage void __exception_irq_entry asm_do_IRQ(unsigned int irq, struct pt_regs *regs) { handle_IRQ(irq, regs); }irq就是中断号、
void handle_IRQ(unsigned int irq, struct pt_regs *regs) { __handle_domain_irq(NULL, irq, false, regs); } int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq, bool lookup, struct pt_regs *regs) { struct pt_regs *old_regs = set_irq_regs(regs); unsigned int irq = hwirq; int ret = 0; irq_enter(); #ifdef CONFIG_IRQ_DOMAIN if (lookup) irq = irq_find_mapping(domain, hwirq); #endif /* * Some hardware gives randomly wrong interrupts. Rather * than crashing, do something sensible. */ if (unlikely(!irq || irq >= nr_irqs)) { ack_bad_irq(irq); ret = -EINVAL; } else { generic_handle_irq(irq); } irq_exit(); set_irq_regs(old_regs); return ret; } int generic_handle_irq(unsigned int irq) { struct irq_desc *desc = irq_to_desc(irq);//根据终端号取出对应的irq_dest数据 if (!desc) return -EINVAL; generic_handle_irq_desc(desc); return 0; } struct irq_desc *irq_to_desc(unsigned int irq) { return (irq < NR_IRQS) ? irq_desc + irq : NULL; }/* 我们使用request_irq的时候就会将相关的信息填入到 这个irq_desc[]数组中*/
struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = { [0 ... NR_IRQS-1] = { .handle_irq = handle_bad_irq, .depth = 1, .lock = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock), } };//好了,这里就会执行我们注册进来的中断处理函数了。
static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc) { desc->handle_irq(irq, desc); }/**************** 到这里分析完毕 ***************************************/