暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

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

txp玩Linux 2022-06-19
841

前言:

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

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

往期文章:

Linux内核学习(一)

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

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

我扫你,还是你扫我?
文章转载自txp玩Linux,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论