Linux内核源代码情景分析-异常
来源:互联网 发布:小牛数据恢复破解版 编辑:程序博客网 时间:2024/04/29 11:06
一、异常初始化
中断向量表的IDT的初始化
void __init trap_init(void){#ifdef CONFIG_EISAif (isa_readl(0x0FFFD9) == 'E'+('I'<<8)+('S'<<16)+('A'<<24))EISA_bus = 1;#endifset_trap_gate(0,÷_error);set_trap_gate(1,&debug);......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,&spurious_interrupt_bug);set_trap_gate(16,&coprocessor_error);set_trap_gate(17,&alignment_check);set_trap_gate(18,&machine_check);set_trap_gate(19,&simd_coprocessor_error); ......}
static void __init set_trap_gate(unsigned int n, void *addr){_set_gate(idt_table+n,15,0,addr);}
Selector为_KERNEL_CS。P为1;DPL为00;DT为0;TYPE为15,陷阱门。Offset就是异常处理函数的偏移。
二、异常响应
异常一般发生在用户态,在内核态能触发的唯一异常就是缺页异常。 我们以缺页异常为例。
1、执行异常处理函数之前
如果异常发生在用户态,则会形成如下图:
图 异常处理和中断处理系统堆栈对照图
(1)、CPU根据具体的中断向量(本例中为14),从中断向量表IDT中找到相应的表项,而该表项应该是一个陷阱门。 首先把用户态堆栈的SS,用户堆栈的ESP,EFLAGS,用户空间的CS,EIP存入到系统堆栈中(从TSS中获取)。如果所发生的异常产生出错代码的话,就把这个出错代码也压入堆栈。在中断处理中,堆栈的这个位置存放的中断请求号。
(2)、CPU根据陷阱门的设置到达了异常处理函数。
ENTRY(page_fault)pushl $ SYMBOL_NAME(do_page_fault)jmp error_code把do_page_fault压入堆栈中。在中断处理中,堆栈的这个位置存放的ES。然后跳到error_code。
error_code:pushl %dspushl %eaxxorl %eax,%eaxpushl %ebppushl %edipushl %esipushl %edxdecl %eax//eax = -1pushl %ecxpushl %ebxcldmovl %es,%ecxmovl ORIG_EAX(%esp), %esi//获取错误码存放在esi中movl ES(%esp), %edi//获得异常处理函数存放在edi中movl %eax, ORIG_EAX(%esp) //将-1存放在原来存放错误码的位置movl %ecx, ES(%esp) //将es存放在原来存放异常函数的位置,这样就和中断一样了movl %esp,%edxpushl %esi//把错误码压入堆栈,做为异常处理函数的参数pushl %edx//把堆栈的指针也压入堆栈,做为异常处理函数的参数movl $(__KERNEL_DS),%edxmovl %edx,%dsmovl %edx,%esGET_CURRENT(%ebx)call *%edi //执行异常处理函数addl $8,%esp //跳过刚刚压入堆栈做为参数的两个值jmp ret_from_exception经过了error_code,异常处理系统堆栈中,do_page_fault变成了es,出错代码变成了-1。异常处理和中断处理的堆栈基本一样。
2、执行异常处理函数,do_page_fault
asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)//刚刚压入堆栈的参数{struct task_struct *tsk;struct mm_struct *mm;struct vm_area_struct * vma;unsigned long address;unsigned long page;unsigned long fixup;int write;siginfo_t info;/* get the address */__asm__("movl %%cr2,%0":"=r" (address));tsk = current;/* * We fault-in kernel-space virtual memory on-demand. The * 'reference' page table is init_mm.pgd. * * NOTE! We MUST NOT take any locks for this case. We may * be in an interrupt or a critical region, and should * only copy the information from the master page table, * nothing more. */if (address >= TASK_SIZE)goto vmalloc_fault;mm = tsk->mm;info.si_code = SEGV_MAPERR;/* * If we're in an interrupt or have no user * context, we must not take the fault.. */if (in_interrupt() || !mm)goto no_context;down(&mm->mmap_sem);vma = find_vma(mm, address);if (!vma)goto bad_area;if (vma->vm_start <= address)goto good_area;if (!(vma->vm_flags & VM_GROWSDOWN))goto bad_area;if (error_code & 4) {/* * accessing the stack below %esp is always a bug. * The "+ 32" is there due to some instructions (like * pusha) doing post-decrement on the stack and that * doesn't show up until later.. */if (address + 32 < regs->esp)goto bad_area;}if (expand_stack(vma, address))goto bad_area;/* * Ok, we have a good vm_area for this memory access, so * we can handle it.. */good_area:info.si_code = SEGV_ACCERR;write = 0;switch (error_code & 3) {default:/* 3: write, present */#ifdef TEST_VERIFY_AREAif (regs->cs == KERNEL_CS)printk("WP fault at %08lx\n", regs->eip);#endif/* fall through */case 2:/* write, not present */if (!(vma->vm_flags & VM_WRITE))goto bad_area;write++;break;case 1:/* read, present */goto bad_area;case 0:/* read, not present */if (!(vma->vm_flags & (VM_READ | VM_EXEC)))goto bad_area;}/* * If for any reason at all we couldn't handle the fault, * make sure we exit gracefully rather than endlessly redo * the fault. */switch (handle_mm_fault(mm, vma, address, write)) {case 1:tsk->min_flt++;break;case 2:tsk->maj_flt++;break;case 0:goto do_sigbus;default:goto out_of_memory;}/* * Did it hit the DOS screen memory VA from vm86 mode? */if (regs->eflags & VM_MASK) {unsigned long bit = (address - 0xA0000) >> PAGE_SHIFT;if (bit < 32)tsk->thread.screen_bitmap |= 1 << bit;}up(&mm->mmap_sem);return; .......}
3、执行异常处理函数之后
error_code中最后jmp ret_from_exception。
ret_from_exception:#ifdef CONFIG_SMPGET_CURRENT(%ebx)movl processor(%ebx),%eaxshll $CONFIG_X86_L1_CACHE_SHIFT,%eaxmovl SYMBOL_NAME(irq_stat)(,%eax),%ecx# softirq_activetestl SYMBOL_NAME(irq_stat)+4(,%eax),%ecx# softirq_mask#elsemovl SYMBOL_NAME(irq_stat),%ecx# softirq_activetestl SYMBOL_NAME(irq_stat)+4,%ecx# softirq_mask#endifjne handle_softirq //如果有软中断,先处理软中断ENTRY(ret_from_intr) //这个函数我们在,执行中断处理函数之后已经讲过了GET_CURRENT(%ebx)movl EFLAGS(%esp),%eax# mix EFLAGS and CSmovb CS(%esp),%altestl $(VM_MASK | 3),%eax# return to VM86 mode or non-supervisor?jne ret_with_reschedulejmp restore_allALIGNhandle_softirq:call SYMBOL_NAME(do_softirq)jmp ret_from_intr
0 0
- Linux内核源代码情景分析-异常
- linux内核源代码情景分析
- Linux内核源代码情景分析-内存管理
- Linux内核源代码情景分析-中断上半部
- Linux内核源代码情景分析-系统调用
- Linux内核源代码情景分析-fork()
- Linux内核源代码情景分析-wait()、schedule()
- Linux内核源代码情景分析-execve()
- Linux内核源代码情景分析-exit()
- Linux内核源代码情景分析-强制性调度
- Linux内核源代码情景分析-虚拟文件系统
- Linux内核源代码情景分析-信号
- Linux内核源代码情景分析-共享内存
- Linux内核源代码情景分析-交换分区
- Linux内核源代码情景分析-insmod
- Linux内核源代码情景分析-系统初始化
- Linux内核源代码情景分析笔记
- Linux内核源代码情景分析---第三章 中断、异常和系统调用
- Mysql大小写敏感
- Python标准库:内置函数range(stop) range(start, stop[, step])
- How to fix hung_task_timeout_secs and blocked for more than 120 seconds problem
- 电脑散热风扇轴承分类
- 2015--阿里内推--测试开发--技术二面--(通过)
- Linux内核源代码情景分析-异常
- 在线即时展现 Html、JS、CSS 编辑工具 - JSFiddle
- 安装DevEmul_SDK后检测不到sdk解决方法
- 竞赛游戏 & 亦或应用!!!
- 如何免费进行论文查重
- OpenGL基本图元
- 字符串转整型.
- STM32 CAN 多于8字节的拆包组包协议(一)
- 用1、2、3、3、4、5这六个数字,用java写一个程序,打印出所有不同的排列 要求:"4"不能在第三位,"3"与"5"不能相连。