一个硬中断的完整处理过程【转】

本文涉及的产品
公网NAT网关,每月750个小时 15CU
简介: 转自:http://www.cnblogs.com/super-king/p/3296201.html CPU做的工作: CPU收到中断/异常信号; CPU判断当前CPL级别如果等于3,则导致堆栈切换3->0,堆栈切换过程: a.
转自:http://www.cnblogs.com/super-king/p/3296201.html
CPU做的工作: CPU收到中断/异常信号; CPU判断当前CPL级别如果等于3,则导致堆栈切换3->0,堆栈切换过程: a. CPU从当前TR指向的TSS中读取SS0和ESP0; b. CPU将当前的【SS:ESP】寄存器内容临时保存起来,假设为SSt和ESPt; c. CPU将SS0和ESP0恢复到【SS:ESP】寄存器中; d. CPU将临时保存的SSt和ESPt压入当前的堆栈【SS:ESP】中(其实就是SS0和ESP0指向的堆栈); CPU判断当前CPL级别如果等于0,则不会有2中的步骤; CPU将EFLAGS、CS、EIP依次压入当前的堆栈【SS:ESP】中; 如果当前是异常,则CPU将异常码error code压入当前的堆栈【SS:ESP】中,否则会省略该步骤; 对于中断,CPU清零当前EFLAGS->IF位,即关闭CPU中断使能,对于异常,CPU则不会清零该位; 执行对中断/异常处理程序的调用; 注:对中断/异常处理程序的要求,为了正常的从中断/异常处理程序中返回,中断/异常处理程序必须使用IRET指令返回,该指令会依次出栈EIP、CS和 EFLAGS,比RET多一个EFLAGS,当EFLAGS恢复后,由于原来保存时IF位为1,因此这里相当于CPU中断使能又打开了; Linux内核做的工作 (2.4 kernel): 1. 中断向量表-->common_interrupt: common_interrupt: SAVE_ALL pushl $ret_from_intr SYMBOL_NAME_STR(call_do_IRQ): jmp SYMBOL_NAME_STR(do_IRQ); SAVE_ALL保存所有CPU没有保存的寄存器,由于do_IRQ是函数,这里直接调用jmp,(一般用call来调用函数,call会导致 push EIP,但jmp不会)这样当do_IRQ返回调用ret(ret相当于pop EIP)时,会弹出栈中最后一个元素到EIP,很显然这里就是 ret_from_intr,也就是说,从do_IRQ中返回后,会跳转到ret_from_intr处继续执行; 2. 来到do_IRQ: a. 首先给硬中断计数加1,irq_enter(cpu, irq)也就是:++local_irq_count(cpu);每进入一个硬中断处理函数前,local_irq_count(cpu)计数便被加1,处理完毕后减1; b. 如果当前设备中断处理函数可以在中断打开的情况下运行,则调用sti将EFLAGS.IF置位,打开硬中断使能; c. 调用request_irq注册的设备硬中断处理函数; d. 无论EFLAGS.IF是否为0,都调用cli将EFLAGS.IF清零,将硬中断使能关闭; e. 给硬中断计数减1,irq_enter(cpu, irq);该函数其实就是:--local_irq_count(cpu); f. 如果此时有软中断需要运行(如在前面的硬中断处理函数中调用__cpu_raise_softirq),则进入do_softirq中处理软中断,关于do_softirq中的处理步骤见3; e. do_IRQ执行ret,返回到ret_from_intr。 3. do_softirq: a. 首先判断当前是否还有没有处理完毕的硬中断处理程序或软中断处理程序,如果有,则直接退出该函数。 b.将软中断处理计数加1,local_bh_disable()也就是local_bh_count(cpu)++;每进入do_softirq准备进 行处理软中断前,local_bh_count(cpu)计数便被加1,软中断处理函数处理完毕后,在do_softirq返回前,将其值减1; c. 无论EFLAGS.IF是否为0,都调用cli将EFLAGS.IF清零,将硬中断使能关闭,处理些软中断标志位的问题,随后调用sti将EFLAGS.IF置位,打开硬中断使能; d. 执行软中断处理函数; e. 调用cli将EFLAGS.IF清零,将硬中断使能关闭,处理些软中断标志位的问题; f. 将软中断处理计数减1,local_bh_enable()也就是local_bh_count(cpu)--; g. 返回到2.e中; 4. ret_from_intr: ENTRY(ret_from_intr) GET_CURRENT(%ebx) movl EFLAGS(%esp),%eax movb CS(%esp),%al testl $(VM_MASK | 3),%eax jne ret_with_reschedule jmp restore_all 在这段代码中,通过"testl $(VM_MASK |3),%eax"检测中断前夕寄存器EFLAGS的高6位和代码段寄存器CS的内容,来判断中断前夕CPU是否运行于VM86模式、用户空间还是系统空 间,对VM86这里不讲了,但是我们知道CS最低两位代表着中断发生时CPU的运行级别CPL,我们知道Linux只采用两种运行级别,系统为0,用户为 3,所以,如果CS的最低两位为非0,那就说明中断发生于用户空间。如果中断发生于系统空间,控制就直接转移到restore_all,而如果发生于用户 空间,则转移到ret_with_reschedule。在restore_all中恢复1中保存的寄存器,随后调用iret恢复EIP、CS、 EFLAGS返回到中断发生时的状态。 5. ret_with_reschedule: a. 如果发现当前进程的need_resched==1,则会调用schedule; b. 如果发现还有待需要处理的软中断,则会调用do_softirq; 说明:能够走到ret_with_reschedule处,从4中可知,该中断前一定位于用户层,而且不可能有可中断的硬中断或软中断没有执行,也就是说 到达这里in_interrupt必然为0。为什么这里要说不可能有可中断的硬中断或软中断没有执行呢?可中断的硬中断或软中断必然是被中断或者异常所打 断的,当后者处理完毕后,从4中可知,由于后者发生前位于内核态(也就是可中断的硬中断或软中断处理过程中的那个点),故控制就直接转移到 restore_all,即返回到了可中断的硬中断或软中断被打断时的那个点,继续处理完毕,可见,到达这里,必然不存在可中断的硬中断或软中断未被执 行。 附 注:关于in_interrupt宏,也就是(local_irq_count(__cpu) +local_bh_count(__cpu) !=0)。什么情况下会导致进入do_softriq时,in_interrupt不为0呢?第一种,如果当前正在处理可中断的硬中断处理函数,假设此时 来了另一个通道的硬中断,将导致当前硬中断处理函数被中断,进入do_IRQ,随后处理新来的硬中断处理函数,当处理完毕后,到达do_softirq, 由2中可知,此时local_irq_count(__cpu)被原先的硬中断加1,由于其还没有处理完毕,故--local_irq_count (cpu)还没来得及执行,因此local_irq_count(__cpu)>0,也就是in_interrupt!=0;第二种,如果当前正在 do_softirq中处理软中断处理函数,现在来了个硬中断,将导致当前软中断处理函数被中断,进入do_IRQ,随后处理新来的硬中断处理函数,当处 理完毕后,又来带到了do_softirq,由3中可知,此时local_bh_count(cpu)被前一个do_softirq加1了,但是由于其是 中途被中断的,故local_bh_count(cpu)--还没来得及执行,因此local_bh_count(__cpu)>0,也就是 in_interrupt!=0;第三种就是综合第一种和第二种两种情况。 Linux内核做的工作 (2.6 kernel): 首先介绍中断向量的范围: 0 - 19 (0x0 - 0x13) 非屏蔽中断和异常 20 - 31 (0x14 - 0x1f) Intel 保留 32 - 127 (0x20 - 0x7f) 外部中断 (IRQ) 128 (0x80) 系统调用 129 - 238 (0x81 - 0xee) 外部中断 (IRQ) 239 (0xef) 本地APIC时钟中断 240 (0xf0) 本地APIC高温中断 (P4模型中引人) 241 - 250 (0xf1 - 0xfa) linux 将来使用 251 - 253 (0xfb - 0xfd) 处理器间中断 254 (0xfe) 本地APIC错误中断 255 (0xff) 本地APIC伪中断 (CPU屏蔽某个中断是产生) 在init_IRQ中 for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) { int vector = FIRST_EXTERNAL_VECTOR + i; if (i >= NR_IRQS) break; if (vector != SYSCALL_VECTOR) set_intr_gate(vector, interrupt[i]); } 设置了中断函数的入口地址. 那么 interrupt 在哪那? 在entry.S中有 .data ENTRY(interrupt) .text vector=0 ENTRY(irq_entries_start) RING0_INT_FRAME .rept NR_IRQS ALIGN .if vector CFI_ADJUST_CFA_OFFSET -4 .endif 1: pushl $~(vector) CFI_ADJUST_CFA_OFFSET 4 jmp common_interrupt .data .long 1b .text vector=vector+1 .endr ALIGN //看interrupt 在此初始化,所有中断都会调用到下面的标记 common_interrupt: SAVE_ALL TRACE_IRQS_OFF movl %esp,%eax //栈顶地址被放到eax中 call do_IRQ jmp ret_from_intr CFI_ENDPROC 现在来看 do_IRQ -> __do_IRQ 首先: struct irq_desc *desc = irq_desc + irq; struct irq_desc { ...... struct irqaction * action; //IRQ action list ...... }; 函数中 ...... status |= IRQ_PENDING; /* we _want_ to handle it */ action = NULL; // action NULL //如果没有任何cpu正在处理irq且irq线没有被关闭,action才被赋值 if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) { action = desc->action; status &= ~IRQ_PENDING; /* we commit to handling */ status |= IRQ_INPROGRESS; /* we are handling it */ } desc->status = status; ...... 然后不管怎样如果要处理中断会调用 handle_IRQ_event 其中主循环为 do { ret = action->handler(irq, action->dev_id, regs); //调用具体中断处理函数 if (ret == IRQ_HANDLED) status |= action->flags; retval |= ret; action = action->next; } while (action); struct irqaction { irqreturn_t (*handler)(int, void *, struct pt_regs *); //中断处理函数 unsigned long flags; cpumask_t mask; const char *name; void *dev_id; struct irqaction *next; int irq; struct proc_dir_entry *dir; }; 下面讲一下怎样安装的中断处理函数. 安装中断处理程序主要的函数是 int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), unsigned long irqflags, const char *devname, void *dev_id) 函数中主要会调用 setup_irq 会把分配的 struct irqaction 加入到 struct irq_desc *desc = irq_desc + irq; 中. 其中 ...... #if defined(CONFIG_IRQ_PER_CPU) if (new->flags & IRQF_PERCPU) //可以在cpu上嵌套执行中断处理函数 desc->status |= IRQ_PER_CPU; #endif ......
【作者】 张昺华
【新浪微博】 张昺华--sky
【twitter】 @sky2030_
【facebook】 张昺华 zhangbinghua
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
相关实践学习
高可用应用架构
欢迎来到“高可用应用架构”课程,本课程是“弹性计算Clouder系列认证“中的阶段四课程。本课程重点向您阐述了云服务器ECS的高可用部署方案,包含了弹性公网IP和负载均衡的概念及操作,通过本课程的学习您将了解在平时工作中,如何利用负载均衡和多台云服务器组建高可用应用架构,并通过弹性公网IP的方式对外提供稳定的互联网接入,使得您的网站更加稳定的同时可以接受更多人访问,掌握在阿里云上构建企业级大流量网站场景的方法。 学习完本课程后,您将能够: 理解高可用架构的含义并掌握基本实现方法 理解弹性公网IP的概念、功能以及应用场景 理解负载均衡的概念、功能以及应用场景 掌握网站高并发时如何处理的基本思路 完成多台Web服务器的负载均衡,从而实现高可用、高并发流量架构
目录
相关文章
|
芯片 开发者 SoC
E906的中断系统|学习笔记
快速学习 E906的中断系统
443 0
E906的中断系统|学习笔记
|
3月前
|
安全 Linux Windows
中断处理与特权级转移
中断处理与特权级转移
46 0
|
6月前
|
传感器 调度
什么是中断系统?
一、什么是中断系统 中断系统是计算机系统中的一种机制,它允许外部设备和程序请求处理器的注意力,以便进行特定的操作。当一个中断请求被触发时,处理器会暂停当前正在执行的程序,转而执行与中断相关的程序或服务例程。中断系统可以提高计算机系统的效率和响应速度,因为它允许处理器在等待某些事件的同时执行其他任务。常见的中断包括硬件中断(例如键盘输入、鼠标移动、网络数据传输等)和软件中断(例如操作系统调度、系统调用等)。 二、中断系统的特点 中断系统具有以下特点: 1. 实时性:中断系统能够及时响应外部设备的请求,提高计算机系统的响应速度和效率。 2. 可靠性:中断系统能够保证中断请求的可靠性,确保外部设备的
188 0
|
6月前
|
编译器
中断的解析
中断的解析
64 0
|
8月前
定时中断实验【嵌入式系统】
定时中断实验【嵌入式系统】
64 0
|
10月前
|
编译器
【C51单片机】中断系统之单一外中断应用
【C51单片机】中断系统之单一外中断应用
247 0
|
缓存 编译器 Linux
CPU中断控制和并发处理的内核解析
CPU中断控制和并发处理的内核解析
CPU中断控制和并发处理的内核解析
|
内存技术
【嵌入式】位带操作+咬尾中断+晚到中断 解析
位带操作 什么是位带操作? 对32MB SRAM 别名区的访问映射为对1MB SRAM的bit-band 区的访问。 对32MB 外设别名区的访问映射为对1MB 外设bit-band 区的访问。
628 0
|
芯片
中断系统结构及中断控制详解
本文详解了中断系统结构及中断控制。
427 0
详解中断系统
本文针对地详解了中断系统
217 0

相关实验场景

更多