从物理学的角度看,硬件中断是一种电信号,由硬件设备产生,并直接送入中断控制器输入引脚上,再由中断控制器向处理器发送相应的信号。处理器一旦检测到该信号,便中断自己当前正在处理的工作,转而去处理中断。图6-7是中断处理原理图。
图6-7 中断处理原理
如果中断处理过程非常复杂,可以分成两部分:上半部(top half)和下半部(bottom half)。上半部完成一些急需处理的事务,如从硬件读取信息,下半部完成余下的复杂的运算或逻辑处理。下半部是可中断的,而上半部是不可中断的,处理完毕立即返回。Linux中的中断下半部包括软中断、tasklet机制和工作队列等。
Linux内核中的中断请求队列用irq_desc结构描述:
IRQ服务列表的结构描述为:
不同的设备对应的中断都用一个唯一的整型数据标识,这个整型数据叫作中断号。request_irq函数用于申请中断,原型如下:
int request_irq(unsigned int irq,irq_handler_t handler,unsigned long flags,const char*name,void*dev);
参数irq表示所要申请的硬件中断号。name为中断名称。dev为设备参数。handler为中断处理函数,中断产生时系统会调用该函数:
typedef irqreturn_t(*irq_handler_t)(int,void*);
irq_handler_t的第一个参数为中断号。
flags是申请时的选项,它决定中断处理程序的一些特性:
#define IRQF_DISABLED 0x00000020//处理中断时关闭中断
#define IRQF_SAMPLE_RANDOM 0x00000040//对内核熵池有贡献
#define IRQF_SHARED 0x00000080//多设备共享中断
#define IRQF_PROBE_SHARED 0x00000100
#define IRQF_TIMER 0x00000200//定时器中断
#define IRQF_PERCPU 0x00000400
#define IRQF_NOBALANCING 0x00000800//不受中断平衡影响
#define IRQF_IRQPOLL 0x00001000
#define IRQF_ONESHOT 0x00002000
中断触发方式包括以下类型:
#define IRQF_TRIGGER_NONE 0x00000000//未设置触发方式
#define IRQF_TRIGGER_RISING 0x00000001//上升沿
#define IRQF_TRIGGER_FALLING 0x00000002//下降沿(www.xing528.com)
#define IRQF_TRIGGER_HIGH 0x00000004//高电平
#define IRQF_TRIGGER_LOW 0x00000008//低电平
中断处理程序的返回值有三种:
申请中断时如果需要为设备建立一个中断后处理线程,可以使用request_threaded_irq函数。
handler为主处理函数,thread_fn为中断处理线程函数。如果中断主处理函数handler返回IRQ_WAKE_THREAD,则会唤醒中断线程。假如handler为空,则采用默认的主处理函数irq_default_primary_handler:
free_irq函数用来释放一个中断:
void free_irq(unsigned int irq,void*dev_id);
ARM体系中处理中断的函数为asm_do_IRQ。asm_do_IRQ函数在entry-macro-multi.S中被调用:
asm_do_IRQ定义如下:
generic_handle_irq函数调用了generic_handle_irq_desc函数,进而调用了中断处理函数。
handle_irq函数最终会调用irqaction结构的handler成员,也就是request_irq函数注册的中断处理函数。在中断上下文中,有一些需要注意的地方。中断处理函数应该快速退出并让出处理器,不能与用户空间交换数据,不能调用能引起睡眠的函数,并应确保其内部不会触发阻塞等待。在中断处理函数中保护临界区,不能使用互斥体,因为它们可能导致睡眠,真正需要保护临界区的时候应该使用自旋锁。中断处理函数不必是可重用的。当某中断被执行的时候,在它返回之前,相应的IRQ都被禁止了。因此,与进程上下文代码不同的是,同一中断处理函数的不同实例不可能同时运行在多个处理器上。另外中断处理函数可以被更高优先级IRQ的中断处理函数打断。
禁止与允许中断的函数包括:
void disable_irq(int irq);//禁止单个中断,等待成功返回
void disable_irq_nosync(int irq);//禁止单个中断,不等待返回
void enable_irq(int irq);//允许单个中断
void local_irq_save(unsigned long flags);//禁止所有中断,并保存标志
void local_irq_diable(void);//禁止所有中断
void local_irq_restore(unsigned long flags);//使能所有中断,并恢复标志
void loval_irq_enable(void);//使能所有中断
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。