ImageVerifierCode 换一换
格式:DOCX , 页数:61 ,大小:51.32KB ,
资源ID:9623480      下载积分:12 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/9623480.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(Linux内核的中断机制.docx)为本站会员(b****8)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

Linux内核的中断机制.docx

1、Linux内核的中断机制第五章 Linux内核的中断机制 (By 詹荣开,NUDT) Copyright © 2003 by 詹荣开 E-mail:zhanrk Linux-2.4.0 Version 1.0.0,2003-2-14 摘要:本文主要从内核实现的角度分析了Linux 2.4.0内核的设备中断流程。本文是为那些想要了解Linux I/O子系统的读者和Linux驱动程序开发人员而写的。 关键词:Linux、中断、设备驱动程序 申明:这份文档是按照自由软件开放源代码的精神发布的,任何人可以免费获得、使用和重新发布,但是你没有限制别人重新发布你发布内容的权利。发布本文的目的是希

2、望它能对读者有用,但没有任何担保,甚至没有适合特定目的的隐含的担保。更详细的情况请参阅GNU通用公共许可证(GPL),以及GNU自由文档协议(GFDL)。 你应该已经和文档一起收到一份GNU通用公共许可证(GPL)的副本。如果还没有,写信给: The Free Software Foundation, Inc., 675 Mass Ave, Cambridge,MA02139, USA 欢迎各位指出文档中的错误与疑问。 51 I386的中断与异常 中断通常被分为“同步中断”和异步中断两大类。同步中断是指当指令执行时由CPU控制单元产生的中断,之所以称为“同步中断”是因为只有在一条指令中止执行后

3、CPU才会发出这类中断信号。而异步中断则是指由其他硬件设备依照CPU时钟随机产生的中断信号。 在Intel 80x86 CPU手册中,同步中断和异步中断也被分别称为“异常(Exception)”和“中断(Interrupt)”。Intel又详细地把中断和异常细分为以下几类: (1)中断 1. 可屏蔽中断(Maskable Interrupt):这类中断被送到CPU的INTR引脚,通过清除eflag寄存器的IF标志可以关闭中断。 2. 不可屏蔽中断(Nonmaskable Interrupt):被送到CPU的NMI引脚,通常只有几个危急的事件,如:硬件故障等,才产生不可屏蔽中断信号。寄存器efl

4、ag中的IF标志对这类中断不起作用。 (2)异常 1. 处理器探测异常(Processordetected exception):当CPU执行一条指令时所探测到的一个反常条件所产生的异常。依据CPU控制单元产生异常时保存在内核态堆栈eip寄存器的值,这类异常又可以细分为三种: n 故障(Fault):保存在eip中的值是引起故障的指令地址,因此但异常处理程序结束后,会重新执行那条指令。“缺页故障”是这类异常的一个常见例子。 n 陷阱(Trap):保存在eip中的值是一个指令地址,但该指令在引起陷阱的指令地址之后。只有当没有必要重新执行已执行过的指令时,才会触发trap,其主要用途是调试程序。

5、n 异常中止(Abort):当发生了一个严重的错误,致使CPU控制单元除了麻烦而不能在eip寄存器中保存有意义的值。异常中止通常是由硬件故障或系统表中无效的值引起的。由CPU控制单元发生的这个中断是一种紧急信号,用来把CPU的执行路径切换到异常中止的处理程序,而相应的ISR通常除了迫使受到影响的进程中止外,别无选择。 2. 编程异常(Programmed Exception):通常也称为“软中断(software interrupt)”,是由编程者发出中断请求指令时发生的中断,如:int指令和int3指令。当into(检查溢出)和bound(检查地址越界)指令检查的条件不为真时,也引起编程异常

6、。CPU控制单元把编程异常当作Trap来处理,这类异常有两个典型的用途:一、执行系统调用;二、给调试程序通报一个特定条件。 511 中断向量 每个中断和异常都可以用一个0255之间的无符号整数来标识,Intel称之为“中断向量(Interrupt Vector)”。通常,不可屏蔽中断和异常的中断向量是固定的,而可屏蔽中断的中断向量则可以对中断控制器进行编程来改变。I386 CPU的256个中断向量是这样分配的: 1. 从031这一共32个向量用于异常和不可屏蔽中断。 2. 从3247这一共16个向量用于可屏蔽中断,分别对应于主、从8259A中断控制器的IRQ输入线。 3. 剩余的48255用于

7、标识软中断。 Linux全部使用了047之间的向量。但对于48255之间的软中断向量,Linux只使用了其中的一个,即用于实现系统调用的中断向量128(0x80)。当用户态下的进程执行一条int 0x80汇编指令时,CPU切换到内核态,以服务于系统调用。 Linux在头文件include/asm-i386/hw_irq.h中定义了宏FIRST_EXTERNAL_VECTOR来表示第一个外设中断(即8259A的IRQ0)所对应的中断向量,此外还定义了SYSCALL_VECTOR来表示用于系统调用的中断向量。如下所示: /* * IDT vectors usable for external in

8、terrupt sources start * at 0x20: */ #define FIRST_EXTERNAL_VECTOR 0x20 #define SYSCALL_VECTOR 0x80 512 I386的IDT i386 CPU的IDT表一共有256项,分别对应每一个中断向量。每一个表项就是一个中断描述符,用以描述相对应的中断向量,中断向量就是该描述符在IDT中的索引,每一个中断描述符的大小都是8个字节。根据INTEL的术语,中断描述符也称为“门(Gate)”。 中断描述符有下列4种类型: (1)任务们(Task Gate):包含了一个进程的TSS段选择符。每当中断信号发生时,它被

9、用来取代当前进程的那个TSS段选择符。Linux并没有使用任务们。任务们的格式如下: (2)中断门(Interrupt Gate):中断门中包含了一个段选择符和一个中断处理程序的段内偏移。注意,当I386 CPU穿越一个中断门进入相应的中断处理程序时,它会清除eflag寄存器中的IF标志,从而屏蔽接下来可能发生的可屏蔽中断。 (3)陷阱门(Trap Gate):与中断门类似,不同之处在于CPU通过陷阱门转入中断处理程序时不会清除IF标志。 (4)调用门(Call Gate):Linux并没有使用调用门。 这三种门的格式如图52所示。 513 中断控制器8259A 我们都知道,PC机中都使用两个

10、级联的8359A PIC(Programmable Interrupt Controller,可编程中断控制器,简称PIC)来管理来自系统外设的中断信号。每个8259A PIC提供8根IRQ(Interrupt ReQuest,中断请求,简称IRQ)输入线。在级联方式中,Master 8259A PIC(第一个PIC)的中断信号输入线IR2用于级联Slave 8259A PIC(第二个PIC)的INT引脚,因此两个8259A一共可以提供15根可用的IRQ输入线。如下图所示: 图53 主、从8259A中断控制器的级联 5131 8259A PIC的基本原理 8259A PIC芯片的基本逻辑块图如

11、下所示: “中断屏蔽寄存器”(Interrupt Mask Register,简称IMR)用于屏蔽8259A的中断信号输入,每一位对应一个输入。当IMR中的biti(0i7)位被置1时,相对应的中断信号输入线IRi上的中断信号将被8259A所屏蔽,也即IRi被禁止。 当外设产生中断信号时(由低到高的跳变信号,80x86系统中的8259A是边缘触发的,Edge Triggered),中断信号被输入到“中断请求寄存器”(Interrupt Request Register,简称IRR),并同时看看IMR中的相应位是否已被设置。如果没有被设置,则IRR中的相应位被设置为1,表示外设产生一个中断请求,

12、等待CPU服务。 然后,8259A的优先级仲裁部分从IRR中选出一个优先级最高中断请求。优先级仲裁之后,8259A就通过其INT引脚向CPU发出中断信号,以通知CPU有外设请求中断服务。CPU在其当前指令执行完后就通过他的INTA引脚给8259A发出中断应答信号,以告诉8259A,CPU已经检测到有中断信号产生。 8259A在收到CPU的INTA信号后,将优先级最高的那个中断请求在ISR寄存器(InService Register,简称ISR)中对应的bit置1,表示该中断请求已得到CPU的服务,同时IRR寄存器中的相应位被清零重置。 然后,CPU再向8259A发出一个INTA脉冲信号,825

13、9A在收到CPU的第二个INTA信号后,将中断请求对应的中断向量放到数据总线上,以供CPU读取。CPU读到中断向量后,就可以装入执行相应的中断处理程序。 如果8259A工作在AEOI(Auto End Of Interrupt,简称AEOI)模式下,则当他收到CPU的第二个INTA信号时,它就自动重置ISR寄存器中的相应位。否则,ISR寄存器中的相应位就一直保持为1,直到8259A显示地收到来自于CPU的EOI命令。 5132 8259A的I/O端口 Master 8259A的IO端口地址位0x20和0x21,Slave 8259A的IO端口地址为0xA0和0xA1。对这些IO端口进行读写操作

14、时的功能如下表所示: I/O Port Addrss Read/Write Function Port A(0x20/0xA0) W Initialization Command Word1(ICW1) W Operation Command Word2(OCW2) W Operation Command Word3(OCW3) R Interrupt Request Register(IRR) R In-Service Register(ISR) Port B(0x21/0xA1) W Initialization Command Word2(ICW2) W Initialization C

15、ommand Word3(ICW3) W Initialization Command Word4(ICW4) W Operation Command Word1(OCW1) R Interrupt Mask Register(IMR) 表51 8259A的I/O端口地址列表 5133 初始化8259A 8259A PIC的初始化是通过向其写一系列“初始化命令字”(Initialization Command Word,简称ICW)来实现的。其中,ICW1必须写到Port A(0x20/0xA0)中,而ICW2、ICW3和ICW4则必须写到Port B(0x21/0xA1)中。此外,主、从82

16、59A必须分别进行初始化。 ICW1的格式如下图55所示: ICW2的格式如下图56所示: 在MCS80/85模式下,A15A8指定中断向量地址;而在80x86模式下,T7T3用于指定中断向量地址,而bit2:0则可被设置为0。 ICW3(Master Device)的格式如下: Si(0i7)为1则表示相应的IRi上级联了一个Slave 8259A PIC。 ICW3(Slave Device)的格式如下: Bit7:3总为0,而ID2、ID1、ID0(即bit2:0)用于标识Slave 8259A连接在Master 8259A的哪一根IRQ线上。例如:010就表示Slave 8259A是连

17、接在Master 8259A的IR2上。 ICW4的格式如下: 5134 控制8259A 可以向Port A或Port B写入“控制命令字”(Control Command Word,简称OCW)来控制8259A PIC。有三种类型的OCW,其中OCW1只能被写入到Port B中,OCW2和OCW3只能被写到Port A中。 OCW1(Interrupt Mask Register)的格式如下: M7M0就是IRQ7IRQ0各自对应的屏蔽位。IMR寄存器可以通过向Port B写入OCW1来设置,它的当前值也可以通过读取Port B来得到。 OCW2的格式如下: OCW3的格式如下: 52 Li

18、nux对IDT的初始化 521 定义IDT的数据结构 Linux在include/asm-i386Desc.h头文件中定义了数据结构desc_struct,用来描述i386 CPU中的各种描述符(均为8字节),如下: struct desc_struct unsigned long a,b; ; 基于上述结构,Linux在arch/i386/kernel/traps.c文件中定义了数组idt_table256,来表示中断描述符表IDT,其定义如下: /* * The IDT has to be page-aligned to simplify the Pentium * F0 0F bug w

19、orkaround. We have a special link segment * for this. */ struct desc_struct idt_table256 _attribute_(_section_(.data.idt) = 0, 0, ; 522 对门的操作函数 Linux在arch/i386/kernel/traps.c文件中定义了宏_set_gate(),用来设置一个描述符(即门)的具体值。由于Linux内核代码均在段选择子_KERNEL_CS所指向的内核段中,因此门中的Segment Selector字段总是等于_KERNEL_CS。宏_set_gate()有四个

20、参数:(1)gate_addr:描述符desc_struct结构类型的指针,指定待操作的描述符,通常指向数组idt_table中的某个项。(2)type:描述符类型,对应于门格式中的Type字段。(3)dpl:该描述符的权限级别;(4)addr:中断处理程序入口地址的段内偏移量,由于内核段的起始地址总是为0,因此中断处理程序在内核段中的段内偏移量也就是中断处理程序的入口地址(即核心虚地址)。 宏_set_gate()的源码如下: #define _set_gate(gate_addr,type,dpl,addr) do int _d0, _d1; _asm_ _volatile_ (movw

21、%dx,%axnt movw %4,%dxnt movl %eax,%0nt movl %edx,%1 :=m (*(long *) (gate_addr), =m (*(1+(long *) (gate_addr), =&a (_d0), =&d (_d1) :i (short) (0x8000+(dpl13)+(type8), 3 (char *) (addr),2 (_KERNEL_CS 16); while (0) 由于不同的门其Type字段和DPL字段是固定的,因此Linux又在宏_set_gate()的基础上为不同类型的门分别封装了专用的操作宏,它们同样也在traps.c文件中:

22、(1)中断门的操作宏set_intr_gate(),其源码如下: void set_intr_gate(unsigned int n, void *addr) _set_gate(idt_table+n,14,0,addr); Type=1110(14),dpl=0(ring 0) 其中,参数n是中断向量(被用作IDT表的索引)。参数addr是中断处理程序的入口地址。 (2)陷阱门的操作宏set_trap_gate()。其源码如下: static void _init set_trap_gate(unsigned int n, void *addr) _set_gate(idt_table+n

23、,15,0,addr); Type=1111(15),dpl=0(ring 0) (3)系统门的操作宏set_system_gate()。Linux扩展了INTEL的术语,它将可编程异常所对应的中断描述符称为“系统门”。系统门的DPL字段为3(用户态),因此使得用户进程可以在用户态下(I386运行在ring 3级别)通过int指令或其它指令穿越系统门而进入内核态。 static void _init set_system_gate(unsigned int n, void *addr) _set_gate(idt_table+n,15,3,addr); Type=1111(15),dpl=3(

24、ring 3) (4)调用门的操作宏set_call_gate(),其源码如下: static void _init set_call_gate(void *a, void *addr) _set_gate(a,12,3,addr); Type=1100(12),dpl=3(ring 3) 从上述这四个专用的宏操作实现也可以看出,Linux并没有使用i386 CPU的调用门和任务门,而是仅仅使用了中断门和陷阱门(Linux又将它细分为陷阱门和系统门)两种中断描述符。 523 对IDT表的初始化设置 Linux内核在初始阶段完成了对也是虚存管理机制的初始化后,便调用trap_init()函数和i

25、nit_IRQ()函数对i386 CPU中断机制的核心IDT进行初始化设置。如下: asmlinkage void _init start_kernel(void) trap_init(); init_IRQ(); 其中,函数trap_init()用来对除外设中断(3247)以外的所有处理器保留的中断向量进行初始化。而init_IRQ()函数则用来初始化对应于主、从8259A中断控制器的可屏蔽外设中断3247。 函数trap_init()定义在arch/i386/kernel/traps.c文件中,其源码如下: void _init trap_init(void) #ifdef CONFIG_

26、EISA if (isa_readl(0x0FFFD9) = E+(I8)+(S16)+(A24) EISA_bus = 1; #endif set_trap_gate(0,÷_error); set_trap_gate(1,&debug); set_intr_gate(2,&nmi); set_system_gate(3,&int3); /* int3-5 can be called from all */ set_system_gate(4,&overflow); set_system_gate(5,&bounds); set_trap_gate(6,&invalid_op);

27、 set_trap_gate(7,&device_not_available); set_trap_gate(8,&double_fault); set_trap_gate(9,&coprocessor_segment_overrun); set_trap_gate(10,&invalid_TSS); set_trap_gate(11,&segment_not_present); set_trap_gate(12,&stack_segment); set_trap_gate(13,&general_protection); set_trap_gate(14,&page_fault); set_

28、trap_gate(15,&spurious_interrupt_bug); set_trap_gate(16,&coprocessor_error); set_trap_gate(17,&alignment_check); set_trap_gate(18,&machine_check); set_trap_gate(19,&simd_coprocessor_error); set_system_gate(SYSCALL_VECTOR,&system_call); /* * default LDT is a single-entry callgate to lcall7 for iBCS *

29、 and a callgate to lcall27 for Solaris/x86 binaries */ set_call_gate(&default_ldt0,lcall7); set_call_gate(&default_ldt4,lcall27); /* * Should be a barrier for any external CPU state. */ cpu_init(); #ifdef CONFIG_X86_VISWS_APIC superio_init(); lithium_init(); cobalt_init(); #endif 从上述代码可以看出,trap_init

30、()函数的核心就是做两件事:(1)设置IDT的前20个表项,这是因为在031这32个CPU保留的异常中断向量中,Intel仅定义了前20个(019)中断向量,而中断向量2031这12个中断向量则保留待以后扩展。(2)设置中断向量SYSCALL_VECTOR(0x80),以用于系统调用的实现。 函数init_IRQ()实现在arch/i386/kernel/i8259.c文件中。它负责初始化IDT表中的后224个中断描述符即中断向量32256所对应的中断描述符(除了用于syscall的中断向量0x80)。其源码如下: void _init init_IRQ(void) int i; #ifndef CONFIG_X86_VISWS_APIC init_ISA_irqs(); #else init_VISWS_APIC_irqs(); #endif /* * Cover the whole vector space, no vector can escape * us. (some of these will be overridden and become * special SMP interrupts) */ for (i = 0; i NR_IRQS; i+) int vector = FIRST_EXTERNA

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1