IRQ0中断处理全过程
简述中断控制器的工作过程

简述中断控制器的工作过程
简述中断控制器的工作过程指的是对中断控制器如何工作的简要描述。
中断控制器是计算机中负责管理中断信号的硬件设备。
中断控制器的工作过程主要包括以下几个步骤:
1.接收中断信号:中断控制器时刻监听来自各个设备的中断信号。
一旦某个
设备产生中断信号,中断控制器会接收并处理这个信号。
2.判断优先级:中断控制器会根据预设的优先级规则判断接收到的中断信号
的优先级。
优先级高的中断会被优先处理。
3.保存上下文:中断控制器在处理中断之前,需要保存当前CPU的执行上下
文(如寄存器状态等),以便在处理完中断后能恢复到原来的执行状态。
4.处理中断:中断控制器将控制权交给相应的中断处理程序,由其执行中断
处理操作。
中断处理程序可以包括对发生中断的设备进行服务、数据处理等操作。
5.恢复上下文:中断处理程序执行完毕后,中断控制器会恢复CPU的执行上
下文,继续执行原来的任务。
6.发送中断结束信号:中断控制器在完成中断处理后,会向CPU发送一个中
断结束信号,通知CPU可以继续执行后续的任务。
以上是简述中断控制器工作过程的一般步骤,实际的中断处理过程可能因计算机架构和操作系统而有所不同。
BIOS中断大全(2)详解BIOS指令参数

BIOS中断大全(2)13)、功能0CH 功能描述:查觅入口参数:AH=0CH CH=柱点的低8位CL(7-6位)=柱面的高2位DH=磁头DL=80H~0FFH:硬盘出口参数:CF=0——操作成功,AH=00H, 中国彩吧3d ,否则,AH=状态代码,参见功能号01H中的说明(14)、功能0DH 功能描述:硬盘系统(13)、功能0CH功能描述:查觅入口参数:AH=0CHCH=柱点的低8位CL(7-6位)=柱面的高2位DH=磁头DL=80H~0FFH:硬盘出口参数:CF=0——操作成功,AH=00H,中国彩吧3d,否则,AH=状态代码,参见功能号01H中的说明(14)、功能0DH功能描述:硬盘系统复位入口参数:AH=0DHDL=80H~0FFH:硬盘出口参数:CF=0——操作成功,AH=00H,否则,AH=状态代码,参见功能号01H 中的说明(15)、功能0EH功能描述:读扇区缓冲区入口参数:AH=0EHES:BX=缓冲区的地址出口参数:CF=0——操作成功,否则,AH=状态代码,参睹功能号01H中的解释(16)、功能0FH功能描述:写扇区慢冲区入口参数:AH=0FHES:BX=徐冲区的地址出口参数:CF=0——操作成功,否则,AH=状态代码,参见功能号01H中的说明(17)、功能10H功能描述:读取驱动器状态入口参数:AH=10HDL=80H~0FFH:硬盘出口参数:CF=0——操作成功,AH=00H,否则,AH=状态代码,参见功能号01H 中的说亮(18)、功能11H功能描述:校准驱动器入口参数:AH=11HDL=80H~0FFH:硬盘出口参数:CF=0——操作成功,AH=00H,否则,AH=状态代码,参见功能号01H 中的说明(19)、功能12H功能描述:节制器RAM诊断入口参数:AH=12H出口参数:CF=0——操作成功,否则,AH=状态代码,参见功能号01H中的说明(20)、功能13H功能描述:把持器驱动诊断出口参数:CF=0——操作成功,否则,AH=状态代码,参见功能号01H中的说明(21)、功能14H功能描述:掌握器内部诊断入口参数:AH=14H出口参数:CF=0——操作成功,否则,AH=状态代码,参见功能号01H中的说明(22)、功能15H功能描述:读取磁盘类型入口参数:AH=15HDL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘出口参数:CF=1——操作失败,AH=状态代码,参见功能号01H中的说明,否则,AH=00H —未安装驱动器=01H —无改变线支撑的软盘驱动器=02H —带有改变线支持的软盘驱动器=03H —硬盘,CX:DX=512字节的扇区数(23)、功能16H功能描述:读取磁盘变更状态入口参数:AH=16HDL=00H~7FH:软盘出口参数:CF=0——磁盘未改变,AH=00H,否则,AH=06H,参见功能号01H中的说明(24)、功能17H功能描述:设置磁盘种型DL=00H~7FH:软盘AL=00H —未用=01H — 360K在360K驱动器中=02H — 360K在1.2M驱动器中=03H — 1.2M在1.2M驱动器中=04H — 720K在720K驱动器中出口参数:CF=0——操作成功,AH=00H,否则,AH=状态编码,参见功能号01H 中的说明(25)、功能18H功能描述:设置格式化媒体类型入口参数:AH=18HCH=柱面数CL=每磁讲的扇区数DL=00H~7FH:软盘出口参数:CF=0——操作成功,AH=00H,ES:DI=介量类型参数表地址,否则,AH =状态编码,参见功能号01H中的说明(26)、功能19H功能描述:磁头保护,仅在PS/2中有效,在此从略(27)、功能1AH功能描述:格式化ESDI驱动器,仅在PS/2中有效,在此从略3、串行口服务(Serial Port Service——INT 14H)00H —初始化通信口03H —读取通信口状态01H —向通信口输出字符04H —裁减初始化通信口02H —从通信口读入字符(1)、功能00H功能描述:初始化通信口入口参数:AH=00HDX=初始化通信口号(0=COM1,1=COM2,……)AL=初始化参数,参数的说明如下:波特率奇偶位结束位字的位数76543210000 = 110X0 = None0 = 1 bit10 = 7 bits001 = 15001 = Odd1 = 2 bits11 = 8 bits010 = 30011 = Even011 = 600100 = 1200101 = 2400110 = 4800111 = 9600对PS/2,可用INT 14H之功效04H跟05H去始初化其通讯快率大于9600。
Linux中断处理流程

Linux中断处理流程1. 中断处理流程 当中断发⽣时,Linux系统会跳转到asm_do_IRQ()函数(所有中断程序的总⼊⼝函数),并且把中断号irq传进来。
根据中断号,找到中断号对应的irq_desc结构(irq_desc结构为内核中中断的描述结构,内核中有⼀个irq_desc结构的数组irq_desc_ptrs[NR_IRQS]),然后调⽤irq_desc中的handle_irq函数,即中断⼊⼝函数。
我们编写中断的驱动,即填充并注册irq_desc结构。
2. 中断处理数据结构:irq_desc Linux内核将所有的中断统⼀编号,使⽤⼀个irq_desc[NR_IRQS]的结构体数组来描述这些中断:每个数组项对应着⼀个中断源(也可能是⼀组中断源),记录中断⼊⼝函数、中断标记,并提供了中断的底层硬件访问函数(中断清除、屏蔽、使能)。
另外通过这个结构体数组项中的action,能够找到⽤户注册的中断处理函数。
struct irq_desc {unsigned int irq;irq_flow_handler_t handle_irq;struct irq_chip *chip;struct msi_desc *msi_desc;void *handler_data;void *chip_data;struct irqaction *action; /* IRQ action list */unsigned int status; /* IRQ status */unsigned int depth; /* nested irq disables */unsigned int wake_depth; /* nested wake enables */unsigned int irq_count; /* For detecting broken IRQs */unsigned long last_unhandled; /* Aging timer for unhandled count */unsigned int irqs_unhandled;spinlock_t lock;const char *name;} ____cacheline_internodealigned_in_smp;(1)handle_irq:中断的⼊⼝函数(2)chip:包含这个中断的清除、屏蔽、使能等底层函数struct irq_chip {const char *name;unsigned int (*startup)(unsigned int irq);void (*shutdown)(unsigned int irq);void (*enable)(unsigned int irq);void (*disable)(unsigned int irq);void (*ack)(unsigned int irq);void (*mask)(unsigned int irq);void (*mask_ack)(unsigned int irq);void (*unmask)(unsigned int irq);void (*eoi)(unsigned int irq);void (*end)(unsigned int irq);void (*set_affinity)(unsigned int irq,const struct cpumask *dest);int (*retrigger)(unsigned int irq);int (*set_type)(unsigned int irq, unsigned int flow_type);int (*set_wake)(unsigned int irq, unsigned int on);/* Currently used only by UML, might disappear one day.*/#ifdef CONFIG_IRQ_RELEASE_METHODvoid (*release)(unsigned int irq, void *dev_id);#endif/** For compatibility, ->typename is copied into ->name.* Will disappear.*/const char *typename;};(3)action:记录⽤户注册的中断处理函数、中断标志等内容struct irqaction {irq_handler_t handler;unsigned long flags;cpumask_t mask;const char *name;void *dev_id;struct irqaction *next;int irq;struct proc_dir_entry *dir;};3. 中断处理流程总结(1)发⽣中断后,CPU执⾏异常向量vector_irq的代码;(2)在vector_irq⾥⾯,最终会调⽤中断处理C程序总⼊⼝函数asm_do_IRQ();(3)asm_do_IRQ()根据中断号调⽤irq_des[NR_IRQS]数组中的对应数组项中的handle_irq();(4)handle_irq()会使⽤chip的成员函数来设置硬件,例如清除中断,禁⽌中断,重新开启中断等;(5)handle_irq逐个调⽤⽤户在action链表中注册的处理函数。
硬件中断

硬件中断硬件中断概述中断可以用下面的流程来表示:中断产生源 --> 中断向量表 (idt) --> 中断入口 ( 一般简单处理后调用相应的函数) --->do_IRQ--> 后续处理(软中断等工作)如图:具体地说,处理过程如下:1.中断信号由外部设备发送到中断芯片(模块)的引脚2.中断芯片将引脚的信号转换成数字信号传给CPU,例如8259主芯片引脚0发送的是0x203.CPU接收中断后,到中断向量表IDT中找中断向量4.根据存在中断向量中的数值找到向量入口5.由向量入口跳转到一个统一的处理函数do_IRQ6.在do_IRQ中可能会标注一些软中断,在执行完do_IRQ后执行这些软中断。
下面一一介绍。
8259芯片本文主要参考周明德《微型计算机系统原理及应用》和billpan的相关帖子1.中断产生过程(1)如果IR引脚上有信号,会使中断请求寄存器(Interrupt Request Register,IRR)相应的位置位,比如图中, IR3, IR4, IR5上有信号,那么IRR的3,4,5为1(2)如果这些IRR中有一个是允许的,也就是没有被屏蔽,那么就会通过INT 向CPU发出中断请求信号。
屏蔽是由中断屏蔽寄存器(Interrupt Mask Register,IMR)来控制的,比如图中位3被置1,也就是IRR位3的信号被屏蔽了。
在图中,还有4,5的信号没有被屏蔽,所以,会向CPU发出请求信号。
(3)如果CPU处于开中断状态,那么在执行指令的最后一个周期,在INTA上做出回应,并且关中断.(4)8259A收到回应后,将中断服务寄存器(In-Service Register)置位,而将相应的IRR复位:8259芯片会比较IRR中的中断的优先级,如上图中,由于IMR中位3处于屏蔽状态,所以实际上只是比较IR4,I5,缺省情况下,IR0最高,依次往下,IR7最低(这种优先级可以被设置),所以上图中,ISR被设置为4.(5)在CPU发出下一个INTA信号时,8259将中断号送到数据线上,从而能被CPU接收到,这里有个问题:比如在上图中,8259获得的是数4,但是CPU需要的是中断号(并不为4),从而可以到idt找相应的向量。
IRQ设置

3、在“PCI Configuration”设置页面的上半部分有一项“Slot 3
IRQ”,它就是第三根PCI插槽所分配的IRQ位置。系统默认为“Auto”(自动)。
4、将光标移到“Slot 3
4设置为“IRQ11”等。
6、此外,如果你使用的主板集成有老一代的ISA插槽的话,还要在“PCI IRQ Resource
Exclusion”选项中,对个别的ISA总线硬件所需的IRQ进行更改设置。
7、完成设置后选择保存设置并重新启动电脑,就可以向烦人的IRQ冲突说BYEBYE啦。
二、什么是IRQ冲突
IRQ的数目有限,一部电脑虽然一共有16个IRQ(从IRQ0至IRQ15),但是其中很多IRQ已经预先分配给特定的硬件,具体如下:
IRQ0:系统计时器
IRQ1:键盘
IRQ2:可设置中断控制卡
IRQ3:COM2(串行接口2)
IRQ4:COM1(串行接口1)
IRQ设置
---------------------------------------------------------------
一、什么是IRQ
IRQ的全称是“Interupt
ReQuest”,即“中断要求”。当电脑内的周边硬件需要处理器去执行某些工作时,该硬件就会发出一个硬件信号,通知处理器工作,而这个信号就是IRQ。那为什么叫做“中断”呢?“中断”的意思是即使处理器正在执行其他工作,当它收到周边设备传来的中断信号时,处理器也会停下来,优先处理这个信号代表的工作,完成后再继续处理之前未完成的工作。
IRQ15:Secondary(从)IDE控制器
中断和中断处理流程

中断和中断处理流程转⾃:1. 中断概念中断是指由于接收到来⾃外围硬件(相对于中央处理器和内存)的异步信号或来⾃软件的同步信号,⽽进⾏相应的硬件/软件处理。
发出这样的信号称为进⾏中断请求(interrupt request,IRQ)。
硬件中断导致处理器通过⼀个上下⽂切换(context switch)来保存执⾏状态(以程序计数器和程序状态字等寄存器信息为主);软件中断则通常作为CPU指令集中的⼀个指令,以可编程的⽅式直接指⽰这种上下⽂切换,并将处理导向⼀段中断处理代码。
中断在计算机多任务处理,尤其是实时系统中尤为有⽤。
这样的系统,包括运⾏于其上的操作系统,也被称为“中断驱动的”(interrupt-driven)。
中断是⼀种使CPU中⽌正在执⾏的程序⽽转去处理特殊事件的操作,这些引起中断的事件称为中断源,它们可能是来⾃外设的输⼊输出请求,也可能是计算机的⼀些异常事故或其它内部原因。
中断:在运⾏⼀个程序的过程中,断续地以“插⼊”⽅式执⾏⼀些完成特定处理功能的程序段,这种处理⽅式称为中断。
2. 中断的作⽤并⾏操作硬件故障报警与处理⽀持多道程序并发运⾏,提⾼计算机系统的运⾏效率⽀持实时处理功能3. 术语按中断源进⾏分类:发出中断请求的设备称为中断源。
按中断源的不同,中断可分为1. 内中断:即程序运⾏错误引起的中断2. 外中断:即由外部设备、接⼝卡引起的中断3. 软件中断:由写在程序中的语句引起的中断程序的执⾏,称为软件中断允许/禁⽌(开/关)中断: CPU通过指令限制某些设备发出中断请求,称为屏蔽中断。
从CPU要不要接收中断即能不能限制某些中断发⽣的⾓度,中断可分为1. 可屏蔽中断:可被CPU通过指令限制某些设备发出中断请求的中断,那是不是意味着进中断时disable整个中断,其实disable的都是可屏蔽中断?2. 不可屏蔽中断:不允许屏蔽的中断如电源掉电中断允许触发器:在CPU内部设置⼀个中断允许触发器,只有该触发器置“1”,才允许中断;置“0”,不允许中断。
IRQ中断处理流程

IRQ中断处理流程基于Linux2.6.30.4分析IRQ中断的处理流程。
1.中断⼊⼝/* arch/arm/kenel/entry-armv.S*/b vector_irq + stubs_offset2.vector_irqvector_stub 宏展开即为vector_irq, 参考。
/** Interrupt dispatcher*/vector_stub irq, IRQ_MODE, 4.long __irq_usr @ 0 (USR_26 / USR_32).long __irq_invalid @ 1 (FIQ_26 / FIQ_32).long __irq_invalid @ 2 (IRQ_26 / IRQ_32).long __irq_svc @ 3 (SVC_26 / SVC_32)3.__irq_user__irq_usr:usr_entry /*3.1*/kuser_cmpxchg_check#ifdef CONFIG_TRACE_IRQFLAGSbl trace_hardirqs_off#endifget_thread_info tsk /*3.2*/#ifdef CONFIG_PREEMPT/**r8<--old preempt_count*r7<--preempt_count+1*preempt_count<--r7*/ldr r8, [tsk, #TI_PREEMPT] @ get preempt countadd r7, r8, #1 @ increment itstr r7, [tsk, #TI_PREEMPT]#endifirq_handler /*3.3*/#ifdef CONFIG_PREEMPT/**r0<--new preempt_count*preempt<--old preempt_count*/ldr r0, [tsk, #TI_PREEMPT]str r8, [tsk, #TI_PREEMPT]teq r0, r7strne r0, [r0, -r0]#endif#ifdef CONFIG_TRACE_IRQFLAGSbl trace_hardirqs_on#endifmov why, #0b ret_to_user /*3.4*/UNWIND(.fnend )ENDPROC(__irq_usr)3.1__user_entry.macro usr_entryUNWIND(.fnstart )UNWIND(.cantunwind ) @ don't unwind the user space/* DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs));*/sub sp, sp, #S_FRAME_SIZE /**/stmib sp, {r1 - r12}ldmia r0, {r1 - r3}add r0, sp, #S_PC @ here for interlock avoidancemov r4, #-1 @ """"""""str r1, [sp] @ save the "real" r0 copied@ from the exception stack@@ We are now ready to fill in the remaining blanks on the stack: @@ r2 - lr_<exception>, already fixed up for correct return/restart@ r3 - spsr_<exception>@ r4 - orig_r0 (see pt_regs definition in ptrace.h)@@ Also, separately save sp_usr and lr_usr@stmia r0, {r2 - r4}/*“^”符号表⽰访问user mode的寄存器*/stmdb r0, {sp, lr}^@@ Enable the alignment trap while in kernel mode@alignment_trap r0@@ Clear FP to mark the first stack frame@zero_fp.endm这⾥⾯⽤到pt_regs结构保存栈上的数据,8字节对齐/** This struct defines the way the registers are stored on the* stack during a system call. Note that sizeof(struct pt_regs)* has to be a multiple of 8.*/struct pt_regs {long uregs[18];};与之相关的宏定义如下DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs));DEFINE(S_R0, offsetof(struct pt_regs, ARM_r0)); DEFINE(S_R1, offsetof(struct pt_regs, ARM_r1)); DEFINE(S_R2, offsetof(struct pt_regs, ARM_r2)); DEFINE(S_R3, offsetof(struct pt_regs, ARM_r3)); DEFINE(S_R4, offsetof(struct pt_regs, ARM_r4)); DEFINE(S_R5, offsetof(struct pt_regs, ARM_r5)); DEFINE(S_R6, offsetof(struct pt_regs, ARM_r6)); DEFINE(S_R7, offsetof(struct pt_regs, ARM_r7)); DEFINE(S_R8, offsetof(struct pt_regs, ARM_r8)); DEFINE(S_R9, offsetof(struct pt_regs, ARM_r9)); DEFINE(S_R10, offsetof(struct pt_regs, ARM_r10)); DEFINE(S_FP, offsetof(struct pt_regs, ARM_fp)); DEFINE(S_IP, offsetof(struct pt_regs, ARM_ip)); DEFINE(S_SP, offsetof(struct pt_regs, ARM_sp)); DEFINE(S_LR, offsetof(struct pt_regs, ARM_lr)); DEFINE(S_PC, offsetof(struct pt_regs, ARM_pc)); DEFINE(S_PSR, offsetof(struct pt_regs, ARM_cpsr)); DEFINE(S_OLD_R0, offsetof(struct pt_regs, ARM_ORIG_r0)); #define ARM_cpsr uregs[16]#define ARM_pc uregs[15]#define ARM_lr uregs[14]#define ARM_sp uregs[13]#define ARM_ip uregs[12]#define ARM_fp uregs[11]#define ARM_r10 uregs[10]#define ARM_r9 uregs[9]#define ARM_r8 uregs[8]#define ARM_r7 uregs[7]#define ARM_r6 uregs[6]#define ARM_r5 uregs[5]#define ARM_r4 uregs[4]#define ARM_r3 uregs[3]#define ARM_r2 uregs[2]#define ARM_r1 uregs[1]#define ARM_r0 uregs[0]#define ARM_ORIG_r0 uregs[17]macos3.2 get_thread_info tsktsk即r9寄存器的别名,内核中为寄存器声明的别名如下/** These are the registers used in the syscall handler, and allow us to* have in theory up to 7 arguments to a function - r0 to r6.** r7 is reserved for the system call number for thumb mode.** Note that tbl == why is intentional.** We must set at least "tsk" and "why" when calling ret_with_reschedule.*/scno .req r7 @ syscall numbertbl .req r8 @ syscall table pointerwhy .req r8 @ Linux syscall (!= 0)tsk .req r9 @ current thread_infoget_thread_info tsk的作⽤是获取sp地址保存在tsk(r9)中,即r9中保存当前任务的thread_info结构的地址。
linux中断处理之IRQ中断

linux中断处理之IRQ中断本文系本站原创,欢迎转载!转载请注明出处:/------------------------------------------一:前言在前一个专题里曾分析过所有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 1if (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;// 分配一个irqactionaction = (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线的中断放在一起处理可以提高效率,且使中断处理程序不必重入举例说明:如果CPU A接收到一个中断信号.回一个ACK,设置c,假设此时末有这个中断线的中断处理程序在处理,继而会将标志位设为IRQ_ INPROGRESS.转去中断处理函数执行.如果此时,CPU B检测到了这条IRQ线的中断信号.它会回一个ACK.设置IRQ_PENDING.但时此时这条IRQ线的标志为IRQ_ INPROGRESS.所以,它会进经过goto out退出.如果cpu A执行完了中断处理程序,判断它的标志线是否为IRQ_PENDING.因为CPU B已将其设为了IRQ_PENDING.所以继续循环一次.直到循环完后,清除IRQ_INPROGRESS标志注意上述读写标志都是加锁的.linux采用的这个方法,不能不赞一个*^_^*继续看代码:asmlinkage int handle_IRQ_event(unsigned int irq,struct pt_regs *regs, struct irqaction *action){int status = 1; /* Force the "do bottom halves" bit */int ret, retval = 0;//如果没有设置SA_INTERRUPT.将CPU 中断打开//应该尽量的避免CPU关中断的情况,因为CPU屏弊本地中断,会使//中断丢失if (!(action->flags & SA_INTERRUPT))local_irq_enable();//遍历运行中断处理程序do {ret = action->handler(irq, action->dev_id, regs);if (ret == IRQ_HANDLED)status |= action->flags;retval |= ret;action = action->next;} while (action);if (status & SA_SAMPLE_RANDOM)add_interrupt_randomness(irq);//关中断local_irq_disable();return retval;}可能会有这样的疑问.如果在一根中断线上挂上了很多个中断处理程序,会不会使这一段程序的效率变得很低下呢?事实上,我们在写驱动程序的过程中,都会首先在中断处理程序里判断设备名字与设备ID,只有条件符合的设备中断才会变处理.三:软中断为了提高中断的响应速度,很多操作系统都把中断分成了两个部份,上半部份与下半部份.上半部份通常是响应中断,并把中断所得到的数据保存进下半部.耗时的操作一般都会留到下半部去处理.接下来,我们看一下软中断的处理模型:Start_kernel() à softirq_init();在softirq_init()中会注册两个常用类型的软中断,看具体代码:void __init softirq_init(void){open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);}//参数含义:nr:软中断类型action:软中断处理函数data:软中断处理函数参数void open_softirq(int nr, void (*action)(struct softirq_action*), void *data){softirq_vec[nr].data = data;softirq_vec[nr].action = action;}static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp;struct softirq_action{void (*action)(struct softirq_action *);void *data;};在上面的代码中,我们可以看到:open_softirq()中.其实就是对softirq_vec数组的nr项赋值.softirq_vec是一个32元素的数组,实际上linux内核只使用了六项. 如下示:enum{HI_SOFTIRQ=0,TIMER_SOFTIRQ,NET_TX_SOFTIRQ,NET_RX_SOFTIRQ,SCSI_SOFTIRQ,TASKLET_SOFTIRQ}另外.如果使软中断能被CPU调度,还得要让它激活才可以.激活所使用的函数为__raise_softirq_irqoff()代码如下:#define __raise_softirq_irqoff(nr) do { local_softirq_pending() |= 1UL << (nr); } while (0)这个宏使local_softirq_pending的nr位置1好了,经过open_softirq()à local_softirq_pending()后,我们来看下软中断怎么被CPU调度.继续上面中断处理的代码.在处理完硬件中断后,会调用irq_exit().这就是软中断的入口点了,我们来看下#define irq_exit() \do { \preempt_count() -= IRQ_EXIT_OFFSET; \//注意了,软中断不可以在硬件中断上下文或者是在软中断环境中使用哦^_^//softirq_pending()的判断,注意我们上面分析过的_raise_softirqoff().它判断当前cpu有没有激活软中断if (!in_interrupt() && softirq_pending(smp_processor_id())) \do_softirq(); \preempt_enable_no_resched(); \} while (0)跟踪进do_softirq()asmlinkage void do_softirq(void){__u32 pending;unsigned long flags;//在硬件中断环境中,退出if (in_interrupt())return;//禁止本地中断,不要让其受中断的影响local_irq_save(flags);pending = local_softirq_pending();//是否有软中断要处理?if (pending)__do_softirq();//恢复CPU中断local_irq_restore(flags);}转入__do_softirq()asmlinkage void __do_softirq(void){struct softirq_action *h;__u32 pending;int max_restart = MAX_SOFTIRQ_RESTART;int cpu;pending = local_softirq_pending();//禁止软中断,不允许软中断嵌套local_bh_disable();cpu = smp_processor_id();restart:/* Reset the pending bitmask before enabling irqs *///把挂上去的软中断清除掉,因为我们在这里会全部处理完local_softirq_pending() = 0;//开CPU中断local_irq_enable();//softirq_vec:32元素数组h = softirq_vec;//依次处理挂上去的软中断do {if (pending & 1) {//调用软中断函数h->action(h);rcu_bh_qsctr_inc(cpu);}h++;pending >>= 1;} while (pending);//关CPU 中断local_irq_disable();pending = local_softirq_pending();//在规定次数内,如果有新的软中断了,可以继续在这里处理完if (pending && --max_restart)goto restart;//依然有没有处理完的软中断,为了提高系统响应效率,唤醒softirqd进行处理if (pending)wakeup_softirqd();//恢复软中断__local_bh_enable();}从上面的处理流程可以看到,软中断处理就是调用open_ softirq()的action参数.这个函数对应的参数是软中断本身(h->action(h)),采用这样的形式,可以在改变softirq_action结构的时候,不会重写软中断处理函数在进入了软中断的时候,使用了in_interrupt()来防止软中断嵌套,和抢占硬中断环境。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
static struct hw_interrupt_type i8259A_irq_type = {
"XT-PIC",
startup_8259A_irq,
shutdown_8259A_irq,
do_8259A_IRQ,
enable_8259A_irq,
disable_8259A_irq
};
do_8259A_IRQ()是个重要的函数,是个IRQ响应处理程序。
同一个文件下,去掉那些不相关的部分,如下:
static void do_8259A_IRQ(unsigned int irq, struct pt_regs * regs)
{ struct irqaction * action;
{
bh_base[nr] = routine;
atomic_set(&bh_mask_count[nr], 0);
bh_mask |= 1 << nr;
}
就是简单的设置bh_base和bh_mask.
看看这些的定义:
atomic_t bh_mask_count[32];
unsigned long bh_active = 0;
s),
对于IRQ0来说,它会间接的调用timer_interrupt(),上面已经讲过。
④timer_interrupt(\arch\i386\kernel\Time.c)是内核接收到IRQ0之后使用的函数,从
CPU时间截记数器中读取一些属性值。如果有值,就调用do_timer_interrup
t(同一个文件中)。
irq_desc[i].depth = 0;
if (i < 16) {
irq_desc[i].handler = &i8259A_irq_type;
} else {
irq_desc[i].handler = &no_irq_type;
}
}
}
把IRQ小于16的handler都置为了i8259A_irq_type。
比较重要的调用就是init_ISA_irqs();
③init_ISA_irqs()在同一个文件里
void init_ISA_irqs (void)
{ int i;
for (i = 0; i < NR_IRQS; i++) {
irq_desc[i].status = IRQ_DISABLED;
irq_desc[i].action = 0;
irq_desc[irq].handler->handle(irq, ®s);
if (1) {
if (bh_active & bh_mask)
do_bottom_half();/*处理下半部分*/
}
}
其实这里irq_desc[irq].handler->handle(irq, ®s);就是do_8259A_IRQ(irq,®
⑥时钟中断的另外一个下半底tqueue_bh(\kernel\Sched.c)
run_old_timers();
run_timer_list();
}
④void run_old_timers(void)
{/*
*简单的遍历处理timer_table数组的列表项,
*如果定时器已经触发,就调用相关的函数。*/
struct timer_struct *tp;
unsigned long mask;
⑤在do_timer_interrupt中又调用do_timer(\kernel\sched.c)。其他东西现在不感兴趣
。
⑥void do_timer(struct pt_regs * regs)
{
/*全局变量jiffies加1*/
(*(unsigned long *)&jiffies)++;
lost_ticks++;/*丢失的定时器滴答的数目*/
IRQ0中断处理全过程
1:系统注册IRQ0(时钟中断)的下半部分的处理过程。
在\kernel\sched.c的sched_init函数中
init_bh(TIMER_BH, timer_bh);/*TIMER_BH==0*/
init_bh(TQUEUE_BH, tqueue_bh);/*TQUEUE_BH==2*/
对于IRQ0,其实就是timer_interrupt函数。
不,其实现在还不是,等time_init()完就基本差不多了。
⑤time_init()在\arch\i386\kernel\Time.c中
感兴趣的就是setup_x86_irq(0, &irq0);作用就是给IRQ0增加一个操作。即timer_inte
}
mark_bh()函数把bh_active相应的位置1.
这里看到了没有,其实时钟中断有两个下半部分。
一个TIMER_BH,一个TQUEUE_BH。
4现在来看看系统执行下半底过程。do_bottom_half
①do_bottom_half(\kernel\softirq.c)做一些处理后,就调用run_bottom_halves();
continue;
timer_active &= ~mask;
tp->fn();
sti();
}
}
老的系统定时器如下组织:
timer_table[32] timer_struct
Timer_struct
Timer_active
31 0
⑤run_timer_list()也差不多是这样组织的。
Timer_head timer_list timer_list
unsigned long bh_mask = 0;
void (*bh_base[32])(void);
bh_base[]
31 bh_active 0
Bottom half handler(timer_bh)
31 bh_mask 0
不好意思,画的这么难看:P
如果bh_mask的第N位被置为1,则表明bh_base[]中的第N个指针指向了一个Bottom half
rrupt().
static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0, "timer",
NULL, NULL};
现在关于IRQ0的初始化完成了。变成如下图示:
irq_desc_t irq_desc[]
struct hw_interrupt_type
{
int status;
int cpu = smp_processor_id();
irq_enter(cpu, irq);
status = 1; /* Force the "do bottom halves" bit */
do {
if (!(action->flags & SA_INTERRUPT))
②void run_bottom_halves(void)
{
unsigned long active;
void (**bh)(void);
active = get_active_bhs();/*取得激活的下半底*/
clear_active_bhs(active);/*清那些激活的下半底*/
bh = bh_base;
mark_bh(TIMER_BH);/*标记timer的下半部*/
if (!user_mode(regs))
lost_ticks_system++;/*如果在系统模式下
*在系统模式下丢失的滴答数加1
*/
if (tq_timer)/*判断定时器队列中有无任务*/
mark_bh(TQUEUE_BH);/*如果有,标记定时器队列的下半部分*/
do {
if (active & 1)
(*bh)();
bh++;
active >>= 1;
} while (active);
}
③对于IRQ0来说,(*bh)();就是timer_bh(\kernel\Sched.c);
void timer_bh(void)
{/*这是定时器真正的下半部分*/
update_times();/*更新统计数字*/
if (status & SA_SAMPLE_RANDOM)
add_interrupt_randomness(irq);
__cli();
irq_exit(cpu, irq);
return status;
}
只注意这里一个函数:
action->handler(irq, action->dev_id, regs);
例程。
如果bh_active的第N位被置为1,则表明一旦调度进程许可,立即调用第N个Bottom hal
f例程。
bh_mask_count[]跟踪为每个下半部分提出的enable/disable请求嵌套对的数组。
2.系统初始化时钟中断(IRQ0)
①先看看start_kernel(\init\main.c)吧
}
handle_IRQ_event(irq, regs, action);
}
④handle_IRQ_event()也在同一个文件里。
int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqacti