中断子系统7_中断出口处理
// 中断公共入口 1.1 common_interrupt: //所有可屏蔽中断函数的公共入口 SAVE_ALL //寄存器入栈 movl %esp,%eax // eax保存栈顶指针 call do_IRQ //中断处理函数 jmp ret_from_intr //从中断返回 // 从中断返回 // 函数主要任务: // 1.确定中断发生前的运行模式 // 1.1 恢复内核执行路径 // 1.1.1 检查是否内核抢占,执行内核抢占 // 1.1.2 恢复硬件上下文 // 1.1.3.iret // 1.2 恢复用户执行路径 // 1.2.1 处理信号 // 1.2.2 恢复硬件上下文 // 1.2.3 iret 2.1 ret_from_intr: GET_THREAD_INFO(%ebp) //当前进程threadinfo的指针存入寄存器EBX中 movl EFLAGS(%esp), %eax movb CS(%esp), %al testl $(VM_MASK | 3), %eax //cs,eflags确定中断发生前的运行模式:1.VM86,用户空间,2.内核空间 jz resume_kernel ENTRY(resume_userspace) //恢复用户空间 cli //关本cpu中断,防止中断退出时被打断 movl TI_flags(%ebp), %ecx //threadinfo->flags andl $_TIF_WORK_MASK, %ecx //处理挂起的信号 jne work_pending jmp restore_all //恢复硬件上下文 ENTRY(resume_kernel) //恢复内核空间 cli //关本cpu中断,防止中断退出时被打断 cmpl $0,TI_preempt_count(%ebp) //内核抢占计数器 jnz restore_all //内核抢占计数!=0,不可以发生内核抢占,恢复之前的内核执行路径 need_resched: //否则,检查当前进程是否设置need_resched标志 movl TI_flags(%ebp), %ecx testb $_TIF_NEED_RESCHED, %cl //没有设置TIF_NEED_RESCHED标志 jz restore_all //恢复程序执行 testl $IF_MASK,EFLAGS(%esp) jz restore_all call preempt_schedule_irq //内核抢占 jmp need_resched //重调度 // 恢复中断发生前程序状态 // 注:iret中断服务程序的最后一条指令。IRET指令将推入堆栈的段地址和偏移地址弹出, // 使程序返回到原来发生中断的地方。其作用是从中断中恢复中断前的状态,具体作用有如下三点: // 1.恢复IP(instruction pointer):(IP)←((SP)+1:(SP)),(SP)←(SP)+2 // 2.恢复CS(code segment):(CS)←((SP))+1:(SP)),(SP)←(SP)+2 // 3.恢复中断前的PSW(program status word),即恢复中断前的标志寄存器的状态。 // (FR)←((SP)+1:(SP)),(SP)←(SP)+2 2.2 #define RESTORE_ALL \ RESTORE_REGS \ addl $4, %esp; \ 1: iret;
//ret_from_intr 流程:
// 内核抢占入口 // 函数主要任务: // 1.设置preempt_active,表示当前进程正在发生内核抢占 // 1.1 防止递归调用preempt_schedule_irq // 2.开中断 // 3.调度 // 4.进程再次恢复执行,关中断,清除preempt_active,表示内核抢占完成 // 5.检查是否再次需要内核抢占 2.3 asmlinkage void __sched preempt_schedule_irq(void) { struct thread_info *ti = current_thread_info(); need_resched: //设置preempt_active,表示正在发生内核抢占 add_preempt_count(PREEMPT_ACTIVE); //在进入preempt_schedule_irq之前中断被关闭,此处开中断 local_irq_enable(); //调度 schedule(); //此进程恢复执行,关闭中断 local_irq_disable(); //清除preempt_active,表示内核抢占完成 sub_preempt_count(PREEMPT_ACTIVE); //检查是否需要内核抢占 barrier(); if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) goto need_resched; } // 处理挂起的信号 // 函数主要任务: // 1.检查是否需要重调度 // 1.1 调度 // 1.2 恢复执行后,检查信号是否已经被处理 // 1.2.1 如果已经被处理,恢复进程路径 // 1.2.2 重复路径1 // 2.执行信号处理函数 // 3.恢复硬件上下文 2.4 work_pending: testb $_TIF_NEED_RESCHED, %cl //检查是否需要重调度 jz work_notifysig // 先执行进程调度 work_resched: call schedule cli //关中断 movl TI_flags(%ebp), %ecx andl $_TIF_WORK_MASK, %ecx //恢复进程执行后,检查信号是否已经被处理 jz restore_all //如果信号已经被处理,则直接恢复进程硬件上下文 testb $_TIF_NEED_RESCHED, %cl //检查是否需进程调度 jnz work_resched work_notifysig: //处理挂起的信号 testl $VM_MASK, EFLAGS(%esp) movl %esp, %eax //检查是否在vm86模式 jne work_notifysig_v86 xorl %edx, %edx call do_notify_resume //执行信号处理函数 jmp restore_all
//中断嵌套
// 参考 深入理解linux内核
// 关于x86平台下中断嵌套的讨论 // http://blog.focus-linux.net/?p=50