Linux内核学习(三)之asm.s和trap.c的关联!

前言:

大家好,今天给大家解析一下Linux中断处理流程里面asm.s和trap.c里面源码,对于Linux中断工作流程不清楚的朋友,可以看上篇文章,这里再提示一下asm.s和trap.c的源码目录:

109559ceb4ecd4eea7bbb63981b5a2cc.png
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函数开始:

392f64711494a39952c23e43093e9eac.png 3c6fcfe4841ff8886b953a7f4ff6257e.png

没错这里有一个异常初始化接口:

trap_init();

我为什么先讲这个呢,主要是让大家明白主线,然后后面要让asm.s和trap.c里面的c函数接口关联起来;我们来看一下trap_init()接口里面主要干了啥:

bffce5e4dc9b5ddafff3f82cb67f5905.png

这里提示一下这个接口:

//idt就是中断描述表,这里就是往表里填中断号和中断程序的地址
#define set_trap_gate(n,addr) \
 _set_gate(&idt[n],15,0,addr)

这个时期的内核中断号范围是:0~16,后期的中断号作为扩展保留使用,这个我们可以从这段代码看出来。关于这个中断号的具体含义可以参考下面这个:

9db161d40ca271e8279c91c5974de256.png

我们再来追一下真正_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的汇编代码:

e5215ca6bb01df8bb31c64dd46d24e60.png

好了,下面来看asm.s里面的汇编代码,具体的汇编代码语法,你不用过多去关注,大概结合刚才的思维图,过一下整个过程:

64ae780154a62cc88c2cf77e85901cea.png

我这里举个无错误码的代码来分析,有错误码的,您可以参考这个来分析:

_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则进行出栈,位置刚好相反!这里刚好结合上篇的中断工作流程来看,在进行执行中断服务函数之前,就准备我们上面的压栈操作,然后等中断服务函数执行完毕后,出栈,回到中断之前的位置!

afd86dd9f930a0372110d04b39d165d8.png

同时这里我们可以看到在执行单步中断调试时,就会跳转到no_error_code这里,而:

pushl $_do_int3

是把函数地址压入栈,这一切都是在为执行c语言函数do_int3做准备,我们可以来这个接口在哪里被定义:

44a8ad5521040ecc4407712d32650158.png

没错,它是在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

往期文章:

Linux内核学习(一)

Linux内核学习之Linux中断工作流程框架!

对了,个人朋友圈,已经开放,坑位有限,时常分享一些非技术性的东西,感兴趣的,可以来唠唠嗑,交个朋友;技术方面的也行,大家相互学习,共同进步:三人行,必有我师焉!

1ef9202041ae8e8b13f1fe1400bc1afe.png
我扫你,还是你扫我?
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值