中断处理过程示意图_Linux中断处理
简介
1.基于Linux0.11代码进行分析。
2.中断类型分类以及具体的中断。
3.中断向量的注册。
4.中断处理流程。
5.各类型中断的具体执行流程。
中断的类型及具体的种类
Linux0.11内核注释
1.可屏蔽硬件中断。优先级较低,可以被忽略或者延后处理,通常有键盘,打印机。
2.不可屏蔽硬件中断。CPU必须无条件响应,优先级非常的高,通常有电源断电,内存校验错误。
3.软件中断--错误。缺页异常?内存访问时产生缺页异常中断,在中断处理程序中实际分配内存,然后在缺页中断处理完成后,继续访问内存。
4.软件中断--陷阱。常见的例子有系统调用,int 0x80,首先会调用中断处理程序,处理完成后,会继续执行后面的指令。
5.软件中断--放弃。常见的例子有除零,该错误发生后,调用中断处理程序,中断处理程序中会产生SIGFPE信号,程序可通过注册对应的信号处理函数处理该信号。
中断向量的注册
1.源码在head.s这个文件中。
2.0x20-0x2f是硬件中断,在head.s中初始化为ignore_int后,后续的硬件初始化过程中会初始化其中断处理函数。
3.中断向量存储在全局的中断向量数组结构中,该数组长度256,每个元素8个字节,在head.s文件中定义。在system.h文件中,定义了设置该数组的接口。代码如下:
// head.s
.align 2
.word 0
idt_descr:
.word 256*8-1 # idt contains 256 entries
.long idt
// system.h
#define _set_gate(gate_addr,type,dpl,addr) \
__asm__ ("movw %%dx,%%ax\n\t" \
"movw %0,%%dx\n\t" \
"movl %%eax,%1\n\t" \
"movl %%edx,%2" \
: \
: "i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
"o" (*((char *) (gate_addr))), \
"o" (*(4+(char *) (gate_addr))), \
"d" ((char *) (addr)),"a" (0x00080000))
#define set_intr_gate(n,addr) \
_set_gate(&idt[n],14,0,addr)
#define set_trap_gate(n,addr) \
_set_gate(&idt[n],15,0,addr)
#define set_system_gate(n,addr) \
_set_gate(&idt[n],15,3,addr)
4.在head.s中,调用startup_32函数过程中调用setup_idt函数将全局的中断描述符初始化为ignore_int。代码如下
setup_idt:
lea ignore_int,%edx
movl $0x00080000,%eax
movw %dx,%ax /* selector = 0x0008 = cs */
movw $0x8E00,%dx /* interrupt gate - dpl=0, present */
lea idt,%edi // edi寄存器指向idt数组的起始地址
mov $256,%ecx // 循环256次
rp_sidt:
movl %eax,(%edi)
movl %edx,4(%edi)
addl $8,%edi // 下标递增
dec %ecx
jne rp_sidt
lidt idt_descr
ret
5.在trap.c文件中,调用trap_init函数注册软件中断。在sched.c中调用sched_init注册2个调度相关的中断,0x20硬件时钟中断,0x80系统调用中断。其余的硬件中断在硬件初始化时注册。以下是部分代码:
void sched_init(void)
{
set_intr_gate(0x20,&timer_interrupt);
set_system_gate(0x80,&system_call);
}
void trap_init(void)
{
int i;
// 设置除操作出错的中断向量值。
set_trap_gate(0,÷_error);
set_trap_gate(1,&debug);
set_trap_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);
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_trap_gate(15,&reserved);
set_trap_gate(16,&coprocessor_error);
// 下面把int17-47的陷阱门先均设置为reserved,以后各硬件初始化时会重新设置自己的陷阱门。
for (i=17;i<48;i++)
set_trap_gate(i,&reserved);
// 设置协处理器中断0x2d(45)陷阱门描述符,并允许其产生中断请求。设置并行口中断描述符。
set_trap_gate(45,&irq13);
outb_p(inb_p(0x21)&0xfb,0x21); // 允许8259A主芯片的IRQ2中断请求。
outb(inb_p(0xA1)&0xdf,0xA1); // 允许8259A从芯片的IRQ3中断请求。
set_trap_gate(39,¶llel_interrupt); // 设置并行口1的中断0x27陷阱门的描述符。
}
tips:如果想要知道中断处理函数在哪里注册,注册的函数是什么,可搜索system.h文件中的定义的_set_intr_gate, _set_trap_gate, _set_system_gate函数被调用的地方。
中断的处理流程
在分析和阅读源码前,先尝试思考通用的中断处理逻辑。
1.硬件中断。硬件中断通常来自于外部硬件触发。此时进程可能在任意一个状态(用户态执行用户代码,或者在执行中断)。如果是在执行中断,那么就应该判断当前正在执行的中断和触发中断的优先级,然后确定是否要打断正在执行的中断。
2.软件中断。软件中断来自用户代码主动调用产生,所以此时应该是在用户态执行用户代码。
3.用户态下的中断应该有一样的通用流程,大致是,保存当前正在执行代码的上下文,切换到内核态调用中断处理函数,完成后回到用户态恢复上下文,然后继续执行。
中断处理流程
4.中断处理是在内核态下运行,因此使用的是内核的堆栈,如果中断时正在运行用户态的代码,那么在切到内核态后,将当时的上下文寄存器等信息存在内核中的堆栈中。示意图如下:
中断时的堆栈
5.当在中断情况下发生高优先级的中断时,会在中断过程中使用的堆栈的基础上再次保存中断的上下文,然后执行更高优先级的中断。堆栈示意图如下:
高优先级中断打断当前的中断时的堆栈
中断的具体执行
1.源码主要在asm.s和trap.c这2个文件中。
2.在调用具体的中断处理函数前,都会将段寄存器和ip寄存器入中断栈,这是中断打断的正在运行的进程的上下文。然后将实际要执行的用C实现的中断处理函数push入栈,再将普通的寄存器如eax,ebx等寄存器入栈,下一步将一些段寄存器入栈,最后将返回后执行的指令的栈地址入栈。
Linux0.11完全注释中断堆栈图
3.无返回值的中断以int 0x1,除0的中断举例,代码如下:
divide_error:
pushl $do_divide_error # 这里push实际要调用的函数,下一条指令又将其和eax寄存器的值交换。
# 其目的是为了代码复用,其它中断可以直接调用no_error_code代码段
# do_divide_error在traps.c中实现
no_error_code: # 这里是五出错号处理的入口处。
xchgl %eax,(%esp) # _do_divide_error的地址→eax,eax被交换入栈
pushl %ebx #保存打断进程的寄存器上下文
pushl %ecx
pushl %edx
pushl %edi
pushl %esi
pushl %ebp
push %ds # !!16位的段寄存器入栈后也要占用4个字节。
push %es
push %fs
pushl $0 # "error code" #将数值0作为出错码入栈
lea 44(%esp),%edx # 取对堆栈中原调用返回地址处堆栈指针位置,并压入堆栈。
pushl %edx
movl $0x10,%edx # 初始化段寄存器ds、es和fs,加载内核数据段选择符 0x10是内核栈的段起始地址
mov %dx,%ds
mov %dx,%es
mov %dx,%fs
# 下行上的 * 号表示调用操作数指定地址处的函数,称为间接调用。这句的含义是调用引起本次
# 异常的C处理函数,例如do_divide_error等。
call *%eax
addl $8,%esp
pop %fs
pop %es
pop %ds
popl %ebp
popl %esi
popl %edi
popl %edx
popl %ecx
popl %ebx
popl %eax # 弹出原来eax中的内容
iret # 返回时会取出栈中保存的被打断进程的eip寄存器的值 继续执行后续的指令
4.其它的类似的无返回值的中断处理函数如下:
debug:
pushl $do_int3 # _do_debug C函数指针入栈
jmp no_error_code # 复用no_error_code代码段
5.含error_code的中断和有error_code的中断类似,这里就不粘贴重复的代码了,可以参考源码中的asm.s文件。把握住中断处理的核心逻辑,保存上下文,处理中断,恢复上下文,以及参考源码中的system.h, asm.s, traps.c这几个文件。
6.深究int 0x1除0中断的处理。
void do_divide_error(long esp, long error_code)
{
die("divide error",esp,error_code);
}
static void die(char * str,long esp_ptr,long nr)
{
long * esp = (long *) esp_ptr;
int i;
printk("%s: %04x\n\r",str,nr&0xffff);
// 下行打印语句显示当前调用进程的CS:EIP、EFLAGS和SS:ESP的值。
// EIP:\t%04x:%p\n - esp[1]是段选择符(cs),esp[0]是eip.
// EFLAGS:\t%p\n - esp[2]是eflags
// ESP:\t%04x:%p\n - esp[4]是源ss,esp[3]是源esp
printk("EIP:\t%04x:%p\nEFLAGS:\t%p\nESP:\t%04x:%p\n",
esp[1],esp[0],esp[2],esp[4],esp[3]);
printk("fs: %04x\n",_fs());
printk("base: %p, limit: %p\n",get_base(current->ldt[1]),get_limit(0x17));
if (esp[4] == 0x17) {
printk("Stack: ");
for (i=0;i<4;i++)
printk("%p ",get_seg_long(0x17,i+(long *)esp[3]));
printk("\n");
}
str(i); // 取当前运行任务的任务号
printk("Pid: %d, process nr: %d\n\r",current->pid,0xffff & i);
for(i=0;i<10;i++)
printk("%02x ",0xff & get_seg_byte(esp[1],(i+(char *)esp[0])));
printk("\n\r");
// 前面都是打印调试信息 这里才是真正的处理逻辑 在这里是直接退出 错误码为11
do_exit(11); /* play segment exception */
}
总结
中断处理的分析到这里就告一段落了。首先要对中断进行分类,并且了解每种类型的中断具体有哪些类型。然后了解内核是如何处理中断的,中断处理函数使用的栈都是内核的内存空间,在执行具体的处理函数前,要先保存被中断的进程的上下文,然后再调用具体的处理函数,处理完成后再恢复被中断进程的上下文以继续运行。在这里要说明下,内核中的栈每次起始地址都是一样的,这是因为调用结束后,要么不会返回,要么返回后栈又变空了,所以内核栈是可以重复利用的。
中断处理过程示意图_Linux中断处理相关推荐
- 中断处理过程示意图_聊聊什么是中断机制?
什么是中断 中断其实是一种"中断"事件,中断具体代表什么意思需要考虑它所处的上下文环境和参照对象是谁.考虑事件,我们可以简单把中断抽象为这样一种模型: 当我们分析某种中断事件时,我 ...
- 中断处理过程示意图_中断和中断处理流程
1. 中断概念 中断是指由于接收到来自外围硬件(相对于中央处理器和内存)的异步信号或来自软件的同步信号,而进行相应的硬件/软件处理.发出这样的信号称为进行中断请求(interrupt request, ...
- 中断处理过程示意图_ucore操作系统实验笔记 - Lab1
最近一直都在跟清华大学的操作系统课程,这个课程最大的特点是有一系列可以实战的操作系统实验.这些实验总共有8个,我在这里记录实验中的一些心得和总结. Task1 这个Task主要是为了熟悉Makfile ...
- 中断处理过程示意图_中断及中断处理过程
中断及中断处理过程 1. 中断和异常的概念区别 Intel的官方文档里将中断和异常理解为两种中断当前程序执行的不同机制.这是中断和异常的共同点.不同点在于: 中断(interrupt)是异步的事件,典 ...
- 中断处理过程示意图_PCIe中断方法和系统与流程
本发明实施例涉及通信技术领域,尤其涉及一种PCIe中断方法和系统. 背景技术: PCIe(Peripheral Component Interconnect Express,高速外围组件互联)是最新的 ...
- 中断处理过程示意图_中断服务程序流程图
教学方法.实施步骤 时间分配 教学手段 回 顾 5 " × 2 板书 计算机 投影仪 多媒体课件等 讲 授 40 " × 2 提 问 3 " × 2 小 结 2 &quo ...
- Linux内核设计第五周学习总结 分析system_call中断处理过程
陈巧然原创作品 转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 使用gdb跟踪分析一 ...
- QEMU和KVM 中断处理过程
本文讲述一个网络数据包从到达物理网卡,一直到中断注入给VM的整个过程. 为了讲述清晰,假设宿主物理机有两个物理CPU,分别为CPU0和CPU1.假设GuestOS运行在CPU1上,物理网卡接到数据包后 ...
- x86异常处理与中断机制(3)中断处理过程
上一节讲完了根据中断类型号找中断服务程序的过程,现在着重说明一下更加完整的中断处理过程吧. 本节以8086时代的中断处理过程为例进行说明,主要分两大部分 硬件处理 软件处理 需要注意,这不是绝对的,得 ...
最新文章
- linux sudo命令
- python程序员工作怎样-python程序员待遇如何
- Jenkins搭建的几个坑记下
- [转] 移动平台Html5的viewport使用经验
- 高性能MySQL(3)——创建高性能索引
- android serviceconnection unbind流程,Android unbindService 流程分析
- CSS Scrollbar (滚动条)
- 如何使用JMeter自身代理录制APP测试脚本
- opencv编译问题
- ajaxpro定时刷新页面
- 【丐中丐】废旧光驱改装激光雕刻机
- 社团管理系统用c语言,C语言高校社团管理系统设计报告稿件.doc
- ng-zorro里的“Descriptions描述列表”使用
- “因遭勒索软件攻击,我被认定工作失职开除,并被老东家索赔 21.5 万元”
- 江涛带你玩STM-CubeMx之OLED使用3线SPI和4线SPI驱动详解
- TypeError:object of type 'type' has no len()的一种可能原因
- 山东理工大学pta答案 C语言,山东理工大学高分子化学题库附答案.docx
- Python笔记之用turtle库绘制三角函数和反三角函数的图像(考研党福利)
- 如何查看网页中的密码星号内容
- 尿素和车用尿素的区别
热门文章
- 2018.10.16【校内模拟】长者(主席树)(字符串哈希)
- 一个北京妞写给天下所有女人的信
- Shell实现树莓派通过风扇自动调节CPU温度(附源码与原理图)
- 【Python——类】 同一个类中一个函数里调用另一个函数的方法
- excel计算出均方根值(RMS)+ 均方根误差(RMSE)+标准差(Standard Deviation)
- 谁说菜鸟不会数据分析(入门篇)----- 学习笔记1(数据分析基本概念)
- 全新的文件启动方式 HapiGo 1.0.2(15) 中文版
- 计算机术语横幅迎新,迎新横幅的标语(精选50句)
- spring data jpa配置
- B站推荐的5个自学网站