Download presentation
Presentation is loading. Please wait.
1
中国科学技术大学计算机系 陈香兰(0512-87161312) xlanchen@ustc.edu.cn Spring 2011
Linux操作系统分析 中国科学技术大学计算机系 陈香兰(0512- ) Spring 2011
2
中断和异常
3
为什么会有中断 内核的一个主要功能就是处理硬件外设I/O 处理器速度一般比外设快很多
内核必须处理其他任务,只有当外设真正完成了准备好了时CPU才转过来处理外设IO IO方式: 轮询、中断、DMA等 轮询方式效率不高 中断机制就是满足上述条件的一种解决办法 2018/9/19 Linux OS Analysis
4
查看系统中断信息 cat /proc/interrupts
/proc/interrupts: to display every IRQ vector in use by the system 2018/9/19 Linux OS Analysis
5
主要内容 中断信号的作用和中断信号处理的一般原则 I/O设备如何引起CPU中断 x86 CPU如何在硬件级处理中断信号
Linux内核中软件级中断处理及其数据结构 Linux的软中断、tasklet以及下半部分 2018/9/19 Linux OS Analysis
6
主要内容 中断信号的作用和中断信号处理的一般原则 I/O设备如何引起CPU中断 x86 CPU如何在硬件级处理中断信号
Linux内核中软件级中断处理及其数据结构 Linux的软中断、tasklet以及下半部分 2018/9/19 Linux OS Analysis
7
中断和异常 中断(广义)会改变处理器执行指令的顺序,通常与CPU芯片内部或外部硬件电路产生的电信号相对应
中断——异步的: 由硬件随机产生,在程序执行的任何时候可能出现 异常——同步的: 在(特殊的或出错的)指令执行时由CPU控制单元产生 我们用“中断信号”来通称这两种类型的中断 2018/9/19 Linux OS Analysis
8
中断信号的作用 中断信号提供了一种特殊的方式,使得CPU转去运行正常程序之外的代码
为了做到这这一点, 在进程的内核态堆栈保存程序计数器的当前值(即eip和cs寄存器)以便处理完中断的时候能正确返回到中断点, 并把与中断信号相关的一个地址放入进程序计数器,从而进入中断的处理 2018/9/19 Linux OS Analysis
9
中断信号的处理原则 快! 当内核正在做一些别的事情的时候,中断会随时到来。无辜的正在运行的代码被打断
中断处理程序在run的时候可能禁止了同级中断 中断处理程序对硬件操作,一般硬件对时间也是非常敏感的 内核的目标就是让中断尽可能快的处理完,尽其所能把更多的处理向后推迟 上半部分(top half)和下半部分(bottom half) 2018/9/19 Linux OS Analysis
10
允许不同类型中断的嵌套发生,这样能使更多的I/O设备处于忙状态
尽管内核在处理一个中断时可以接受一个新的中断,但在内核代码中还在存在一些临界区,在临界区中,中断必须被禁止 2018/9/19 Linux OS Analysis
11
中断上下文 中断上下文不同于进程上下文 中断或异常处理程序执行的代码不是一个进程 它是一个内核控制路径,代表了中断发生时正在运行的进程执行
作为一个进程的内核控制路径,中断处理程序比一个进程要“轻”(中断上下文只包含了很有限的几个寄存器,建立和终止这个上下文所需要的时间很少) 2018/9/19 Linux OS Analysis
12
中断上下文举例 分析A,B,C,D在互相抢占上的关系 假设: 2个interrupt context,记为A和B
2个process,记为C和D 1, 假设某个时刻C占用CPU运行,此时A中断发生,C被A抢占,A得以在CPU上执行。 由于Linux不为中断处理程序设置process context,A只能使用 C的kernel stack作为自己的运行栈 D进程 current C进程 B中断 A中断 2018/9/19 Linux OS Analysis A中断发生
13
2 ,无论如何,Linux的interrupt context A绝对不会被某个进程C或者D抢占!!
这是由于所有已经启动的interrupt contexts,不管是interrupt contexts之间切换,还是在某个interrupt context中执行代码的过程,决不可能插入scheduler调度例程的调用。 除非interrupt context主动或者被动阻塞进入睡眠,唤起scheduler,但这是必须避免的,危险性见第3点说明。 D进程 current C进程 B中断 A中断 2018/9/19 Linux OS Analysis A中断发生
14
其次,如果interrupt context A由于阻塞或是其他原因睡眠,外界对系统的响应能力将变得不可忍受
3 ,关于第2点的解释: 首先,interrupt context没有process context,A中断是“借”了C的进程上下文运行的,若允许A“阻塞”或“睡眠”,则C将被迫阻塞或睡眠,仅当A被“唤醒”C才被唤醒;而“唤醒”后,A将按照C在就绪队列中的顺序被调度。这既损害了A的利益也污染了C的kernel stack。 其次,如果interrupt context A由于阻塞或是其他原因睡眠,外界对系统的响应能力将变得不可忍受 2018/9/19 Linux OS Analysis
15
4 ,那么interrupt context A和B的关系又如何呢?
由于可能在interrupt context的某个步骤打开了CPU的IF flag标志,这使得在A过程中,B的irq line已经触发了PIC,进而触发了CPU IRQ pin,使得CPU执行中断B的interrupt context,这是中断上下文的嵌套过程。 5,通常Linux不对不同的interrupt contexts设置优先级,这种任意的嵌套是允许的 当然可能某个实时Linux的patch会不允许低优先级的interrupt context抢占高优先级的interrupt context D进程 current C进程 B中断 开中断 A中断 2018/9/19 Linux OS Analysis A中断发生 B中断发生
16
主要内容 中断信号的作用和中断信号处理的一般原则 I/O设备如何引起CPU中断 x86 CPU如何在硬件级处理中断信号
Linux内核中软件级中断处理及其数据结构 Linux的软中断、tasklet以及下半部分 2018/9/19 Linux OS Analysis
17
中断和异常的分类(Intel文档) 中断分为: 可屏蔽中断(Maskable interrupt)
I/O设备发出的所有中断请求(IRQ)都可以产生可屏蔽中断。 可屏蔽中断可以处于两种状态:屏蔽的(masked)和非屏蔽的(unmasked) 非屏蔽中断(Nonmaskable interrupt) 只有几个特定的危急事件才引起非屏蔽中断。如硬件故障或是掉电 2018/9/19 Linux OS Analysis
18
异常分为: 处理器探测异常 编程异常 由CPU执行指令时探测到一个反常条件时产生,如溢出、除0错等
由编程者发出的特定请求产生,通常由int类指令触发 通常叫做“软中断” 例如系统调用 2018/9/19 Linux OS Analysis
19
对于处理器探测异常,根据异常时保存在内核堆栈中的eip的值可以进一步分为:
故障(fault):eip=引起故障的指令的地址 通常可以纠正,处理完异常时,该指令被重新执行 例如缺页异常 陷阱(trap):eip=随后要执行的指令的地址。 异常中止(abort):eip=??? 发生严重的错误。eip值无效,只有强制终止受影响的进程 2018/9/19 Linux OS Analysis
20
中断向量 每个中断和异常由0~255之间的一个数(8位)来标识,Intel称其为中断向量。 非屏蔽中断的向量和异常的向量是固定的
可屏蔽中断的向量可以通过对中断控制器的编程来改变 2018/9/19 Linux OS Analysis
21
中断的产生 每个能够发出中断请求的硬件设备控制器都有一条称为IRQ(Interrupt ReQuest)的输出线。
中断控制器与CPU的INTR引脚相连 设备 设备 控制器 中断 控制器 CPU INTR IRQ 2018/9/19 Linux OS Analysis
22
中断控制器执行下列动作: 1,监视IRQ线,对引发信号检查 2,如果一个引发信号出现在IRQ线上 3,返回到第一步
a,把此信号转换成对应的中断向量 b,把这个向量存放在中断控制器的一个I/O端口,从而允许CPU通过数据总线读这个向量 c,把引发信号发送到处理器的INTR引脚,即产生一个中断 d,等待,直到CPU应答这个信号;收到应答后,清INTR引脚 3,返回到第一步 2018/9/19 Linux OS Analysis
23
IRQ号和中断向量号 中断控制器对输入的IRQ线从0开始顺序编号
Intel给中断控制器分配的中断向量号从32开始,上述IRQ线对应的中断向量依次是 32+0、32+1、… 可以对中断控制器编程: 修改起始中断向量的值,或 有选择的屏蔽/激活每条IRQ线 屏蔽≠丢失 2018/9/19 Linux OS Analysis
24
有选择的屏蔽/激活IRQ线 ≠全局屏蔽/激活
屏蔽的中断不会丢失 一旦被激活,中断控制器又会将它们发送到CPU 有选择的屏蔽/激活IRQ线 ≠全局屏蔽/激活 前者通过对中断控制器编程实现 后者通过特定的指令操作CPU中的状态字 2018/9/19 Linux OS Analysis
25
I386:开中断和关中断 CPU可以将屏蔽所有的可屏蔽终端 Eflags中的IF标志: 0=关中断; 1=开中断。
内核中使用cli和sti指令分别清除和设置该标志 2018/9/19 Linux OS Analysis
26
传统的中断控制器:8259A 传统的中断控制器使用两片8259A以“级联”的方式连接在一起 每个芯片可以处理最多8个不同的IRQ线
2018/9/19 Linux OS Analysis
27
8259A:设置起始中断向量号 参见init_8259A arch/x86/kernel/i8259_32.c 2018/9/19
Linux OS Analysis
28
8259A:禁止/激活某个IRQ线 arch/x86/kernel/i8259_32.c 取变量的第x个字节 2018/9/19
Linux OS Analysis
29
异常 X86处理器发布了大约20种不同的异常。 某些异常通过硬件出错码说明跟异常相关的信息 内核为每个异常提供了一个专门的异常处理程序
2018/9/19 Linux OS Analysis
30
故障 异 常 处 异 理 常 非屏蔽中断 程 处 序 理 陷阱,断点调试 程 序 陷阱 发 出 的 信 号 故障,缺页 异常中止
2018/9/19 Linux OS Analysis
31
中断描述符表(Interrupt Descriptor Table,IDT)
中断描述符表是一个系统表,它与每一个中断或者异常向量相联系 每个向量在表中有相应的中断或者异常处理程序的入口地址。 每个描述符8个字节,共256项,占用空间2KB 内核在允许中断发生前,必须适当的初始化IDT CPU的idtr寄存器指向IDT表的物理基地址 lidt指令 2018/9/19 Linux OS Analysis
32
IDT包含3种类型的描述符 陷阱门:与中断门类似,但进入陷阱门时,系统不会进入关中断状态
中断门:指定中断处理程序,进入中断门时,系统进入关中断状态 任务门:Linux没有使用任务门 2018/9/19 Linux OS Analysis
33
主要内容 中断信号的作用和中断信号处理的一般原则 I/O设备如何引起CPU中断 x86 CPU如何在硬件级处理中断信号
Linux内核中软件级中断处理及其数据结构 Linux的软中断、tasklet以及下半部分 2018/9/19 Linux OS Analysis
34
中断和异常的硬件处理 进入中断/异常 假定:内核已经初始化,CPU在保护模式下运行 CPU的正常运行:
当执行了一条指令后,cs和eip这对寄存器包含了下一条将要执行的指令的逻辑地址。 在执行这条指令之前,CPU控制单元会检查在运行前一条指令时是否发生了一个中断或者异常。 如果发生了一个中断或异常,那么CPU控制单元执行下列操作: 2018/9/19 Linux OS Analysis
35
2,读idtr寄存器指向的IDT表中的第i项
3,从gdtr寄存器获得GDT的基地址,并在GDT中查找,以读取IDT表项中的段选择符所标识的段描述符 4,确定中断是由授权的发生源发出的。 中断:中断处理程序的特权不能低于引起中断的程序的特权(对应GDT表项中的DPL vs CS寄存器中的CPL) 编程异常:还需比较CPL与对应IDT表项中的DPL 这个描述符指定中断或异常处理程序所在段的基地址 禁止低特权级用户访问特殊的门 只允许从低特权级 “陷入”到高特权级, 反之不可以 2018/9/19 Linux OS Analysis
36
5,检查是否发生了特权级的变化,一般指是否由用户态陷入了内核态。 如果是由用户态陷入了内核态,控制单元必须开始使用与新的特权级相关的堆栈
a,读tr寄存器,访问运行进程的tss段 b,用与新特权级相关的栈段和栈指针装载ss和esp寄存器。这些值可以在进程的tss段中找到 c,在新的栈中保存ss和esp以前的值,这些值指明了与旧特权级相关的栈的逻辑地址 2018/9/19 Linux OS Analysis
37
6,若发生的是故障,用引起异常的指令地址修改cs和eip寄存器的值,以使得这条指令在异常处理结束后能被再次执行
7,在栈中保存eflags、cs和eip的内容 8,如果异常产生一个硬件出错码,则将它保存在栈中 9,装载cs和eip寄存器,其值分别是IDT表中第i项门描述符的段选择符和偏移量字段。这对寄存器值给出中断或者异常处理程序的第一条指定的逻辑地址 2018/9/19 Linux OS Analysis
38
此时的进程内核态堆栈 (注意此进程可以是任意一个进程,中断处理程序不关心这个) 从用户态进 入中断/异常 ss esp 从内核态进
用户态进程上下文 和前次中断保存 ss,esp, eflags,cs和eip esp 从内核态进 入中断/异常 eflags eflags cs cs eip eip 8KB union esp esp Error code esp 进程描述符 进程描述符 2018/9/19 Linux OS Analysis
39
从中断/异常返回 中断/异常处理完后,相应的处理程序会执行一条iret汇编指令,这条汇编指令让CPU控制单元做如下事情:
1,用保存在栈中的值装载cs、eip和eflags寄存器。如果一个硬件出错码曾被压入栈中,那么弹出这个硬件出错码 2,检查处理程序的特权级是否等于cs中最低两位的值(这意味着进程在被中断的时候是运行在内核态还是用户态)。若是,iret终止执行;否则,转入3 2018/9/19 Linux OS Analysis
40
3,从栈中装载ss和esp寄存器。这步意味着返回到与旧特权级相关的栈
4,检查ds、es、fs和gs段寄存器的内容,如果其中一个寄存器包含的选择符是一个段描述符,并且特权级比当前特权级高,则清除相应的寄存器。这么做是防止怀有恶意的用户程序利用这些寄存器访问内核空间 2018/9/19 Linux OS Analysis
41
主要内容 中断信号的作用和中断信号处理的一般原则 I/O设备如何引起CPU中断 x86 CPU如何在硬件级处理中断信号
Linux内核中软件级中断处理及其数据结构 Linux的软中断、tasklet以及下半部分 2018/9/19 Linux OS Analysis
42
中断和异常处理程序的嵌套执行 当内核处理一个中断或异常时,就开始了一个新的内核控制路径
当CPU正在执行一个与中断相关的内核控制路径时,linux不允许进程切换。不过,一个中断处理程序可以被另外一个中断处理程序中断,这就是中断的嵌套执行 2018/9/19 Linux OS Analysis
43
抢占原则 Linux允许中断嵌套的原因 普通进程可以被中断或异常处理程序打断 异常处理程序可以被中断程序打断
中断程序只可能被其他的中断程序打断 Linux允许中断嵌套的原因 提高可编程中断控制器和设备控制器的吞吐量 实现了一种没有优先级的中断模型 2018/9/19 Linux OS Analysis
44
初始化中断描述符表 内核启动中断前,必须初始化IDT,然后把IDT的基地址装载到idtr寄存器中
int指令允许用户进程发出一个中断信号,其值可以是0-255的任意一个向量。 所以,为了防止用户用int指令非法模拟中断和异常,IDT的初始化时要很小心的设置特权级 然而用户进程有时必须要能发出一个编程异常。为了做到这一点,只要把相应的中断或陷阱门描述符的特权级设置成3 2018/9/19 Linux OS Analysis
45
初始化中断描述符表 Linux中的中断门、陷阱门和系统门定义 中断门 系统门 陷阱门
用户态的进程不能访问的一个Intel中断门(特权级为0),所有的中断都通过中断门激活,并全部在内核态 系统门 用户态的进程可以访问的一个Intel陷阱门(特权级为3),通过系统门来激活4个linux异常处理程序,它们的向量是3,4,5和128。因此,在用户态下可以发布int3,into,bound和int $0x80四条汇编指令 陷阱门 用户态的进程不能访问的一个Intel陷阱门(特权级为0),大部分linux异常处理程序通过陷阱门激活 2018/9/19 Linux OS Analysis
46
初始化中断描述符表 下列体系结构相关的函数用来在IDT中设置门 这几个函数都把相应的门中的段 描述符设置成内核代码段的选
择符,偏移字段设置成addr。 下列体系结构相关的函数用来在IDT中设置门 include/asm-x86/desc.h 不同的是系统门中特权级对应 的位DPL被置成3。 2018/9/19 Linux OS Analysis
47
2018/9/19 Linux OS Analysis
48
进入保护模式前IDT表的初始化 arch/x86/boot/pm.c 2018/9/19 Linux OS Analysis
49
IDT的初步初始化(head_32.S) 用ignore_int()函数填充256个idt_table表项
arch/x86/kernel/head_32.S 注意:此后还有关于异常相关入口的调整,使用了宏 2018/9/19 Linux OS Analysis
50
arch/x86/kernel/head_32.S
2018/9/19 Linux OS Analysis
51
Start_kernel中的IDT表初始化
trap_init() init_IRQ() 阅读native_init_IRQ 2018/9/19 Linux OS Analysis
52
异常处理 CPU产生的大部分异常都由linux解释为出错条件。 当一个异常发生时,内核就向引起异常的进程发送一个信号通知它发生了一个反常条件
异常处理有一个标准的结构,由三部分组成 在内核态堆栈中保存大多数寄存器的内容 调用C语言的函数 通过ret_from_exception()从异常处理程序退出 观察entry_32.S,并找到C语言函数的定义之处 2018/9/19 Linux OS Analysis
53
arch/x86/kernel/entry_32.S
arch/x86/mm/fault.c 2018/9/19 Linux OS Analysis
54
arch/x86/kernel/entry_32.S
arch/x86/kernel/trap_32.S 2018/9/19 Linux OS Analysis
55
阅读error_code arch/x86/kernel/entry_32.S 2018/9/19 Linux OS Analysis
56
此时的内核态堆栈 ss esp eflag cs eip orig_eax:0 fs 硬件自动保存 es ds eax 用于传递 ebp
edi esi edx ecx ebx error_code Pointer 返回地址 硬件自动保存 此时的内核态堆栈 用于传递 Errorc_ode 高地址 用于传递 do_xxx,最后为 __KERNEL_PERCPU Error_code拷贝完后,被设为-1 error_code 代码手工压入 低地址 pt_regs指针 esp Thread_info 2018/9/19 Linux OS Analysis
57
pt_regs结构(恢复现场所需的上下文)
include/asm-x86/ptrace.h 栈顶(低地址) 栈底(高地址) 2018/9/19 Linux OS Analysis
58
异常处理 当C函数终止时,根据 堆栈中的返回地址, CPU从call *%edi这条指令 的下一条指令开始继续执行, 即:
ss esp eflag cs eip orgi_eax(-1) fs es ds eax ebp edi esi edx ecx ebx error_code Pointer 返回地址 硬件自动保存 将由iret指令 负责弹出 当C函数终止时,根据 堆栈中的返回地址, CPU从call *%edi这条指令 的下一条指令开始继续执行, 即: jmp ret_from_exception 前面的汇编 手工压入, 将由restore_all 负责弹出 esp 进程描述符 2018/9/19 Linux OS Analysis
59
中断处理 中断跟异常不同,它并不是表示程序出错, 而是硬件设备有所动作,所以不是简单地往当前进程发送一个信号就OK的 主要有三种类型的中断:
I/O设备发出中断请求 时钟中断 处理器间中断(在SMP, Symmetric Multiprocessor上才会有这种中断) 2018/9/19 Linux OS Analysis
60
I/O中断处理程序必须足够灵活以给多个设备同时提供服务
比如几个设备可以共享同一个IRQ线 (2个8359级联也只能提供15根IRQ线,所以外设共享IRQ线是很正常的) 这就意味着仅仅中断向量解决不了全部问题 2018/9/19 Linux OS Analysis
61
灵活性以两种不同的方式达到 IRQ共享: 中断处理程序执行多个中断服务例程(interrupt service routines, ISRs)。每个ISR是一个与单独设备(共享IRQ线)相关的函数 IRQ动态分配:一条IRQ线在可能的最后时刻才与一个设备相关联 2018/9/19 Linux OS Analysis
62
为了保证系统对外部的响应,一个中断处理程序必须被尽快的完成。因此,把所有的操作都放在中断处理程序中并不合适
Linux中把紧随中断要执行的操作分为三类 紧急的(critical) 一般关中断运行。诸如对PIC应答中断,对PIC或是硬件控制器重新编程,或者修改由设备和处理器同时访问的数据 2018/9/19 Linux OS Analysis
63
非紧急的(noncritical) 如修改那些只有处理器才会访问的数据结构(例如按下一个键后读扫描码),这些也要很快完成,因此由中断处理程序立即执行,不过一般在开中断的情况下
2018/9/19 Linux OS Analysis
64
非紧急可延迟的(noncritical deferrable) 如把缓冲区内容拷贝到某个进程的地址空间(例如把键盘缓冲区内容发送到终端处理程序进程)。这些操作可以被延迟较长的时间间隔而不影响内核操作,有兴趣的进程将会等待数据。内核用下半部分这样一个机制来在一个更为合适的时机用独立的函数来执行这些操作 2018/9/19 Linux OS Analysis
65
不管引起中断的设备是什么,所有的I/O中断处理程序都执行四个相同的基本操作 1,在内核态堆栈保存IRQ的值和寄存器的内容
2,为正在给IRQ线服务的PIC发送一个应答,这将允许PIC进一步发出中断 3,执行共享这个IRQ的所有设备的中断服务例程 4,跳到ret_from_intr()的地址 2018/9/19 Linux OS Analysis
66
中断处理示意图 2018/9/19 Linux OS Analysis
67
Linux中的中断向量分配表 2018/9/19 Linux OS Analysis
68
Linux中的设备中断 IRQ号与I/O设备之间的对应关系是在初始化每个设备驱动程序时建立的 2018/9/19
Linux OS Analysis
69
中断处理 系统初始化时,调用init_IRQ()函数用新的中断门替换临时中断门来更新IDT
这段代码在interrupt数组中找到用于建立中断门的中断处理程序地址。 arch/x86/kernel/i8259_32.c 2018/9/19 Linux OS Analysis
70
Interrupt数组的定义(比较隐晦)
arch/x86/kernel/entry_32.S 2018/9/19 Linux OS Analysis
71
因此,每个中断程序入口操作为: 将中断向量入栈 保存所有其他寄存器 调用do_IRQ 跳转到ret_from_intr 2018/9/19
Linux OS Analysis
72
do_IRQ(查看do_IRQ源码) do_IRQ使用的数据结构(体系结构无关):
irq_desc数组包含了NR_IRQS(通常为224)个irq_desc_t描述符 224 Irq_chip 中断控制器处理例程 Irq_desc 每一个中断号具有一个描述符,使用action链表连接共享同一个中断号的多个设备和中断 2018/9/19 Linux OS Analysis
73
查看irq_desc数组的定义和最初的初始化
查看相关数据结构 查看irq_desc数组的定义和最初的初始化 include/linux/irq.h kernel/irq/handle.c 2018/9/19 Linux OS Analysis
74
irqaction数据结构 用来实现IRQ的共享,维护共享irq的特定设备和特定中 断,所有共享一个irq的链接在一个action表中,由中断 描述符中的action指针指向 设置irqaction的函数:setup_irq include/linux/interrupt.h 中断处理程序 链表 2018/9/19 Linux OS Analysis
75
irq_chip数据结构 为特定PIC编写的低级I/O例程 例如8259的 为一个中断设置irq_chip
set_irq_chip_and_handler_name等 arch/x86/kernel/i8259_32.c set_irq_chip 2018/9/19 Linux OS Analysis
76
例如:在init_IRQ(即native_init_IRQ)中,调用的pre_intr_init_hook可能如下定义
arch/x86/mach-default/setup.c 2018/9/19 Linux OS Analysis
77
arch/x86/kernel/i8259_32.c 2018/9/19 Linux OS Analysis
78
又如:make_8259A_irq arch/x86/kernel/i8259_32.c 2018/9/19
Linux OS Analysis
79
irq_flow_handler_t __set_irq_handler设置handle_irq数据项
handle_level_irq 8259 handle_simple_irq handle_fasteoi_irq handle_edge_irq handle_percpu_irq handle_IRQ_event action->handler 2018/9/19 Linux OS Analysis
80
Actionhandle 在setup_irq时,给定 例如 arch/x86/mach-default/setup.c
2018/9/19 Linux OS Analysis
81
小结:中断处理过程 在调用do_IRQ之前,要为中断处理程序保存寄存器 在interrupt数组中定义的中断处理程序中
每个入口地址转换成汇编码是如下的一些指令 interrupt[irq]: pushl $~(vector) jmp common_interrupt 这里对所有的中断处理程序都执行相同的代码 common_interrupt: SAVE_ALL movl %esp,%eax call do_IRQ jmp $ret_from_intr 2018/9/19 Linux OS Analysis
82
从do_IRQ返回后要执行的指令地址ret_from_intr
ss esp eflag cs eip $~(vector) fs es ds eax ebp edi esi edx ecx ebx 返回地址 Pointer 硬件自动保存 do_IRQ()的函数声明 arch/x86/kernel/irq_32.c SAVE_ALL 从do_IRQ返回后要执行的指令地址ret_from_intr esp Thread info do_IRQ执行时内核态的堆栈 2018/9/19 Linux OS Analysis
83
中断处理 do_IRQ()函数的等价代码: int irq = ~regs->orig_ax; //1
irq_desc[irq]->handle_irq(irq, desc); //2 mask_ack_irq(desc, irq); //3 handle_IRQ_event(irq,®s,irq_desc[irq].action);//4 irq_desc[irq].handler->end(irq); //5 处理下半部分 //6 1句取得对应的中断向量 2句调用中断处理句柄,对8259,就是handle_level_irq 3句应答PIC的中断,并禁用这条IRQ线。(为串行处理同类型中断) 4调用handle_IRQ_event()执行中断服务例程,例如timer_interrupt 5句通知PIC重新激活这条IRQ线,允许处理同类型中断 2018/9/19 Linux OS Analysis
84
一个中断服务例程实现一种特定设备的操作, handle_IRQ_evnet()函数依次调用这些设备例程
这个函数本质上执行了如下核心代码: do{ action->handler(irq,action->dev_id,regs); action = action->next; }while (action) 2018/9/19 Linux OS Analysis
85
主要内容 中断信号的作用和中断信号处理的一般原则 I/O设备如何引起CPU中断 x86 CPU如何在硬件级处理中断信号
Linux内核中软件级中断处理及其数据结构 Linux的软中断、tasklet以及下半部分 2018/9/19 Linux OS Analysis
86
软中断、tasklet以及下半部分 对内核来讲,可延迟中断不是很紧急,可以将它们从中 断处理例程中抽取出来,保证较短的中断响应时间
Linux2.6提供了三种方法 可延迟的函数 软中断、tasklet Tasklet在软中断之上实现 一般原则:在同一个CPU上软中断/tasklet不嵌套 软中断由内核静态分配(编译时确定) Tasklet可以在运行时分配和初始化(例如装入一个内核模块时 ) 工作队列( work queues ) 软中断 初始化 激活 屏蔽 执行 tasklet tasklet_hi 下半部分 导致一个相应 的tasklet插入 2018/9/19 Linux OS Analysis
87
一般而言,可延迟函数上可以执行4种操作 初始化:定义一个新的可延迟函数,通常在内核初始化时进行 激活:设置可延迟函数在下一轮处理中执行
屏蔽:有选择的屏蔽一个可延迟函数,这样即使被激活也不会被运行 执行:在特定的时间执行可延迟函数 2018/9/19 Linux OS Analysis
88
软中断 Linux2.6.26使用有限个软中断 优先级0:处理高优先级的 include/linux/interrupt.h
tasklet和下半部分 优先级1:与时钟中断相关的tasklet 优先级2:把数据包传送到网卡 优先级3:从网卡接受数据包 优先级4:块设备相关 优先级5:处理tasklet 优先级6:调度SMP相关 2018/9/19 Linux OS Analysis
89
在softirq_vec中定义 kernel/softirq.c 优先级对应于softirq_vec的 下标
include/linux/interrupt.h 软中断函数 及其参数 2018/9/19 Linux OS Analysis
90
软中断的初始化 初始化软中断函数 分别在softirq_init和net_dev_init、blk_dev_init等中初始化
kernel/softirq.c 例如 kernel/softirq.c net/core/dev.c 2018/9/19 Linux OS Analysis
91
软中断的触发 raise_softirq include/asm-x86/hardirq_32.h 2018/9/19
Linux OS Analysis
92
软中断的检查 local_softirq_pending 在某些特定的时机,会检查是否有软中断被挂起 这种时机,称为检查点
include/linux/irq_cpustat.h local_softirq_pending 在某些特定的时机,会检查是否有软中断被挂起 调用local_bh_enable重新激活软中断时 当do_IRQ完成了I/O中断的处理时 当那个特定的进程ksoftirqd被唤醒时 … 这种时机,称为检查点 2018/9/19 Linux OS Analysis
93
在每个检查点 若有软中断被挂起,就调用do_softirq 判断是否可以执行软中断 若可以,就执行软中断
执行后,若发现又有新的软中断被激活,就唤醒ksoftirqd进程,来触发do_softirq的另一次执行 arch/x86/kernel/irq_32.c 2018/9/19 Linux OS Analysis
94
Ksoftirqd内核线程 kernel/softirq.c …… …… 2018/9/19 Linux OS Analysis
95
Tasklet Tasklet是I/O驱动程序中实现可延迟函数的首选方法
建立在HI_SOFTIRQ和TASKLET_SOFTIRQ等软中断之上 Tasklet和高优先级的tasklet 分别存放在tasklet_vec和tasklet_hi_vec数组中 数组的每一项针对一个CPU,代表这个CPU上的tasklet列表 分别由tasklet_action和tasklet_hi_action处理 找到CPU对应的那个项,遍历执行 2018/9/19 Linux OS Analysis
96
include/linux/interrupt.h
0:enable >0:disable kernel/softirq.c 2018/9/19 Linux OS Analysis
97
Tasklet的使用 当需要使用tasklet时,可以按照如下方法进行
3、可以激活这个tasklet ====这个tasklet被插入task_vec或者task_hi_vec的相应CPU的链表上,将在合适的时机得到处理 2018/9/19 Linux OS Analysis
98
激活tasklet的方法 即将tasklet插入到合适的链表中 Tasklet_schedule Tasklet_hi_schedule
include/linux/interrupt.h 2018/9/19 Linux OS Analysis
99
工作队列和工作线程 相关数据结构 入列 queue_work;queue_delayed_work 工作队列的处理
workqueue_struct;cpu_workqueue_struct work_struct;delayed_work 入列 queue_work;queue_delayed_work 工作队列的处理 run_workqueue worker_thread kernel/workqueue.c include/linux/workqueue.h kernel/workqueue.c 2018/9/19 Linux OS Analysis
100
Linux-2.6.26中,工作者队列的创建情况 “events”工作者队列的使用情况 2018/9/19
Linux OS Analysis
101
从中断和异常返回 中断和异常的终止目的很清楚,即恢复某个程序的执行,但是还有几个问题要考虑 内核控制路径是否嵌套 挂起进程的切换请求
如果仅仅只有一条内核控制路径,那CPU必须切换到用户态 挂起进程的切换请求 如果有任何请求,必须调度;否则,当前进程得以运行 挂起的信号 如果一个信号发送到进程,那必须处理它 等等 2018/9/19 Linux OS Analysis
102
阅读Entry.S中从中断和异常返回的代码
阅读ULK3(中文,188页)中的图 2018/9/19 Linux OS Analysis
103
作业 名词解释:故障和陷阱 2018/9/19 Linux OS Analysis
Similar presentations