《Linux内核的中断机制分析(栢图实验室).doc》由会员分享,可在线阅读,更多相关《Linux内核的中断机制分析(栢图实验室).doc(11页珍藏版)》请在金锄头文库上搜索。
1、Linux内核的中断机制分析一、中断的概念中断是指在CPU正常运行期间,由于内外部事件或由程序预先安排的事件引起的CPU暂时停止正在运行的程序,转而为该内部或外部事件或预先安排的事件服务的程序中去,服务完毕后再返回去继续运行被暂时中断的程序。Linux中通常分为外部中断(又叫硬件中断)和内部中断(又叫异常)。在实地址模式中,CPU把内存中从0开始的1KB空间作为一个中断向量表。表中的每一项占4个字节。但是在保护模式中,有这4个字节的表项构成的中断向量表不满足实际需求,于是根据反映模式切换的信息和偏移量的足够使得中断向量表的表项由8个字节组成,而中断向量表也叫做了中断描述符表(IDT)。在CPU
2、中增加了一个用来描述中断描述符表寄存器(IDTR),用来保存中断描述符表的起始地址。二、中断的请求过程外部设备当需要操作系统做相关的事情的时候,会产生相应的中断。设备通过相应的中断线向中断控制器发送高电平以产生中断信号,而操作系统则会从中断控制器的状态位取得那根中断线上产生的中断。而且只有在设备在对某一条中断线拥有控制权,才可以向这条中断线上发送信号。也由于现在的外设越来越多,中断线又是很宝贵的资源不可能被一一对应。因此在使用中断线前,就得对相应的中断线进行申请。无论采用共享中断方式还是独占一个中断,申请过程都是先讲所有的中断线进行扫描,得出哪些没有别占用,从其中选择一个作为该设备的IRQ。其
3、次,通过中断申请函数申请相应的IRQ。最后,根据申请结果查看中断是否能够被执行。中断机制的核心数据结构是 irq_desc, 它完整地描述了一条中断线 (或称为 “中断通道” )。以下程序源码版本为linux-2.6.32.2。其中irq_desc 结构在 include/linux/irq.h 中定义:typedef void (*irq_flow_handler_t)(unsigned int irq,struct irq_desc *desc);struct irq_desc unsigned int irq; struct timer_rand_state *timer_rand_st
4、ate;unsigned int *kstat_irqs;#ifdef CONFIG_INTR_REMAPstruct irq_2_iommu *irq_2_iommu;#endifirq_flow_handler_t handle_irq; /* 高层次的中断事件处理函数 */struct irq_chip *chip; /* 低层次的硬件操作 */struct msi_desc *msi_desc;void *handler_data; /* chip 方法使用的数据*/void *chip_data; /* chip 私有数据 */struct irqaction *action; /*
5、 行为链表(action list) */unsigned int status; /* 状态 */unsigned int depth; /* 关中断次数 */unsigned int wake_depth; /* 唤醒次数 */unsigned int irq_count; /* 发生的中断次数 */unsigned long last_unhandled; /*滞留时间 */unsigned int irqs_unhandled;spinlock_t lock; /*自选锁*/#ifdef CONFIG_SMPcpumask_var_t affinity;unsigned int nod
6、e;#ifdef CONFIG_GENERIC_PENDING_IRQcpumask_var_t pending_mask;#endif#endifatomic_t threads_active;wait_queue_head_t wait_for_threads;#ifdef CONFIG_PROC_FSstruct proc_dir_entry *dir; /* 在 proc 文件系统中的目录 */#endifconst char *name;/*名称*/ _cacheline_internodealigned_in_smp;I、Linux中断的申请与释放:在, , 实现中断申请接口:re
7、quest_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev);函数参数说明unsigned int irq:所要申请的硬件中断号irq_handler_t handler:中断服务程序的入口地址,中断发生时,系统调用handler这个函数。irq_handler_t为自定义类型,其原型为:typedef irqreturn_t (*irq_handler_t)(int, void *);而irqreturn_t的原型为:typedef enum irqret
8、urn irqreturn_t;enum irqreturn IRQ_NONE,/*此设备没有产生中断*/IRQ_HANDLED,/*中断被处理*/IRQ_WAKE_THREAD,/*唤醒中断*/;在枚举类型irqreturn定义在include/linux/irqreturn.h文件中。unsigned long flags:中断处理的属性,与中断管理有关的位掩码选项,有一下几组值:#define IRQF_DISABLED 0x00000020 /*中断禁止*/#define IRQF_SAMPLE_RANDOM 0x00000040 /*供系统产生随机数使用*/#define IRQF_
9、SHARED 0x00000080 /*在设备之间可共享*/#define IRQF_PROBE_SHARED 0x00000100/*探测共享中断*/#define IRQF_TIMER 0x00000200/*专用于时钟中断*/#define IRQF_PERCPU 0x00000400/*每CPU周期执行中断*/#define IRQF_NOBALANCING 0x00000800/*复位中断*/#define IRQF_IRQPOLL 0x00001000/*共享中断中根据注册时间判断*/#define IRQF_ONESHOT 0x00002000/*硬件中断处理完后触发*/#def
10、ine IRQF_TRIGGER_NONE 0x00000000/*无触发中断*/#define IRQF_TRIGGER_RISING 0x00000001/*指定中断触发类型:上升沿有效*/#define IRQF_TRIGGER_FALLING 0x00000002/*中断触发类型:下降沿有效*/#define IRQF_TRIGGER_HIGH 0x00000004/*指定中断触发类型:高电平有效*/#define IRQF_TRIGGER_LOW 0x00000008/*指定中断触发类型:低电平有效*/#define IRQF_TRIGGER_MASK (IRQF_TRIGGER_H
11、IGH | IRQF_TRIGGER_LOW | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)#define IRQF_TRIGGER_PROBE 0x00000010/*触发式检测中断*/const char *dev_name:设备描述,表示那一个设备在使用这个中断。void *dev_id:用作共享中断线的指针.。一般设置为这个设备的设备结构体或者NULL。它是一个独特的标识, 用在当释放中断线时以及可能还被驱动用来指向它自己的私有数据区,来标识哪个设备在中断 。这个参数在真正的驱动程序中一般是指向设备数据结构的指针.在调用中断处理程序的时候它就
12、会传递给中断处理程序的void *dev_id。如果中断没有被共享, dev_id 可以设置为 NULL。 II、释放IRQvoid free_irq(unsigned int irq, void *dev_id);III、中断线共享的数据结构struct irqaction irq_handler_t handler; /* 具体的中断处理程序 */unsigned long flags;/*中断处理属性*/const char *name; /* 名称,会显示在/proc/interreupts 中 */void *dev_id; /* 设备ID,用于区分共享一条中断线的多个处理程序 */
13、struct irqaction *next; /* 指向下一个irq_action 结构 */int irq; /* 中断通道号 */struct proc_dir_entry *dir; /* 指向proc/irq/NN/name 的入口*/irq_handler_t thread_fn;/*线程中断处理函数*/struct task_struct *thread;/*线程中断指针*/unsigned long thread_flags;/*与线程有关的中断标记属性*/;thread_flags参见枚举型enum IRQTF_RUNTHREAD,/*线程中断处理*/IRQTF_DIED,/
14、*线程中断死亡*/IRQTF_WARNED,/*警告信息*/IRQTF_AFFINITY,/*调整线程中断的关系*/;多个中断处理程序可以共享同一条中断线,irqaction 结构中的 next 成员用来把共享同一条中断线的所有中断处理程序组成一个单向链表,dev_id 成员用于区分各个中断处理程序。根据以上内容可以得出中断机制各个数据结构之间的联系如下图所示:三中断的处理过程Linux中断分为两个半部:上半部(tophalf)和下半部(bottom half)。上半部的功能是登记中断,当一个中断发生时,它进行相应地硬件读写后就把中断例程的下半部挂到该设备的下半部执行队列中去。因此,上半部执行
15、的速度就会很快,可以服务更多的中断请求。但是,仅有登记中断是远远不够的,因为中断的事件可能很复杂。因此,Linux引入了一个下半部,来完成中断事件的绝大多数使命。下半部和上半部最大的不同是下半部是可中断的,而上半部是不可中断的,下半部几乎做了中断处理程序所有的事情,而且可以被新的中断打断!下半部则相对来说并不是非常紧急的,通常还是比较耗时的,因此由系统自行安排运行时机,不在中断服务上下文中执行。 中断号的查看可以使用下面的命令:“cat /proc/interrupts”。Linux实现下半部的机制主要有tasklet和工作队列。小任务tasklet的实现其数据结构为struct tasklet_struct,每一个结构体代表一个独立的小任务,定义如下struct tasklet_structstruct tasklet_struct *next;/*指向下一个链表结构*/unsigned long state;/*小任务状态*/atomic_t count;/*引用计数器*/void (*func)(unsigned long);/*小任务的处理函数*/unsigned long data;/*传递小任务函数的参数*/;state的取值参