前言:
大家好,今天给大家解析一下Linux中断处理流程里面asm.s和trap.c里面源码,对于Linux中断工作流程不清楚的朋友,可以看上篇文章,这里再提示一下asm.s和trap.c的源码目录:
然后asm.s主要是干嘛的:
/*
* asm.s contains the low-level code for most hardware faults.
* page_exception is handled by the mm, so that isn't here. This
* file also handles (hopefully) fpu-exceptions due to TS-bit, as
* the fpu must be properly saved/resored. This hasn't been tested.
*/
asm.s包含大多数硬件故障的低级代码。页异常由mm处理,所以这里没有。这 个文件还处理(希望)由于TS位导致的fpu(浮点运算器)异常,如必须正确保存/解析fpu。这里还没有经过测试。
asm.s和trap.c解析:
在解读这两个文件的源码之前,我先从Linux内核的main函数开始:
没错这里有一个异常初始化接口:
trap_init();
我为什么先讲这个呢,主要是让大家明白主线,然后后面要让asm.s和trap.c里面的c函数接口关联起来;我们来看一下trap_init()接口里面主要干了啥:
这里提示一下这个接口:
//idt就是中断描述表,这里就是往表里填中断号和中断程序的地址
#define set_trap_gate(n,addr) \
_set_gate(&idt[n],15,0,addr)
这个时期的内核中断号范围是:0~16,后期的中断号作为扩展保留使用,这个我们可以从这段代码看出来。关于这个中断号的具体含义可以参考下面这个:
我们再来追一下真正_set_gate代码实现:
#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))
这段汇编代码,您可以不用关注里面的细节,当然这个我会在后面详细展开解析,这里您只要知道这个在Linux内核里面是一种典型嵌入式汇编代码书写的形式,因为内核里面有很多这种写法。
在开始正式分析源码之前,等下您需要结合下面的这副思维框架图去看asm.s的汇编代码:
好了,下面来看asm.s里面的汇编代码,具体的汇编代码语法,你不用过多去关注,大概结合刚才的思维图,过一下整个过程:
我这里举个无错误码的代码来分析,有错误码的,您可以参考这个来分析:
_divide_error:
pushl $_do_divide_error//把一个c语言函数入栈
no_error_code://无错误断码中断
xchgl %eax,(%esp) //我这里举个例子,通过堆栈指针esp1所指的位置,把do_divide_error函数的地址通过交换汇编指令xchg存放到eax寄存器中去
pushl %ebx
pushl %ecx
pushl %edx
pushl %edi
pushl %esi
pushl %ebp
push %ds
push %es
push %fs
pushl $0 # "error code"
lea 44(%esp),%edx
pushl %edx
movl $0x10,%edx
mov %dx,%ds
mov %dx,%es
mov %dx,%fs
call *%eax //调用对应的c函数接口
addl $8,%esp
pop %fs
pop %es
pop %ds
popl %ebp
popl %esi
popl %edi
popl %edx
popl %ecx
popl %ebx
popl %eax
iret
_debug://这是一个中断
pushl $_do_int3 # _do_debug
jmp no_error_code//跳转
上面的push是把寄存器ebx等压入栈中去,pop则进行出栈,位置刚好相反!这里刚好结合上篇的中断工作流程来看,在进行执行中断服务函数之前,就准备我们上面的压栈操作,然后等中断服务函数执行完毕后,出栈,回到中断之前的位置!
同时这里我们可以看到在执行单步中断调试时,就会跳转到no_error_code这里,而:
pushl $_do_int3
是把函数地址压入栈,这一切都是在为执行c语言函数do_int3做准备,我们可以来这个接口在哪里被定义:
没错,它是在trap.c里面进行定义的,那么我们就可以看出:asm.s最终执行的中断服务函数,都在trap.c里面找到它的定义,这就是asm.s和trap.c的关系。
总结:
今天的内容就分享到这里,我们下期见!大家有空可以多多看看上面的源码!加以理解。
文章资料相关参考:
Linux内核0.11完全注释
https://www.bilibili.com/video/BV1tQ4y1d7mospm_id_from=333.337.search-card.all.click
往期文章:
对了,个人朋友圈,已经开放,坑位有限,时常分享一些非技术性的东西,感兴趣的,可以来唠唠嗑,交个朋友;技术方面的也行,大家相互学习,共同进步:三人行,必有我师焉!