.globl ret_from_except ret_from_except: LOAD_MSR_KERNEL(r10,MSR_KERNEL) //将MSR_KERNEL常量设置到MSR,以禁止外部中断 SYNC //Some chip revs have problems here... MTMSRD(r10) //disable interrupts lwz r3,_MSR(r1) //读栈中的MSR[PR],Returning to user mode? andi. r0,r3,MSR_PR beq resume_kernel user_exc_return: //r10 contains MSR_KERNEL here rlwinm r9,r1,0,0,(31-THREAD_SHIFT) //Check current_thread_info()->flags lwz r9,TI_FLAGS(r9) andi. r0,r9,(_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK|_TIF_NEED_RESCHED) bne do_work restore_user: #ifdef CONFIG_PREEMPT b restore resume_kernel: rlwinm r9,r1,0,0,(31-THREAD_SHIFT) /* check current_thread_info->preempt_count */ lwz r0,TI_PREEMPT(r9) cmpwi 0,r0,0 /* if non-zero, just restore regs and return */ bne restore lwz r0,TI_FLAGS(r9) andi. r0,r0,_TIF_NEED_RESCHED beq+ restore andi. r0,r3,MSR_EE /* interrupts off? */ beq restore /* don't schedule if so */ 1: bl preempt_schedule_irq rlwinm r9,r1,0,0,(31-THREAD_SHIFT) lwz r3,TI_FLAGS(r9) andi. r0,r3,_TIF_NEED_RESCHED bne- 1b #else resume_kernel: #endif /* CONFIG_PREEMPT */ do_work: /* r10 contains MSR_KERNEL here */ andi. r0,r9,_TIF_NEED_RESCHED beq do_user_signal do_resched: /* r10 contains MSR_KERNEL here */ ori r10,r10,MSR_EE SYNC MTMSRD(r10) /* hard-enable interrupts */ bl schedule recheck: LOAD_MSR_KERNEL(r10,MSR_KERNEL) SYNC MTMSRD(r10) /* disable interrupts */ rlwinm r9,r1,0,0,(31-THREAD_SHIFT) lwz r9,TI_FLAGS(r9) andi. r0,r9,_TIF_NEED_RESCHED bne- do_reschedandi. r0,r9,_TIF_SIGPENDING beq restore_user do_user_signal: /* r10 contains MSR_KERNEL here */ asmlinkage void __sched preempt_schedule_irq(void){ struct thread_info *ti = current_thread_info(); BUG_ON(ti->preempt_count || !irqs_disabled()); do { add_preempt_count(PREEMPT_ACTIVE); local_irq_enable(); schedule(); local_irq_disable(); sub_preempt_count(PREEMPT_ACTIVE); barrier(); } while (unlikely(test_thread_flag(TIF_NEED_RESCHED))); } asmlinkage void __sched preempt_schedule(void){ struct thread_info *ti = current_thread_info(); //preempt_cout非0的话,就不调用schedule if (likely(ti->preempt_count || irqs_disabled())) return; do { add_preempt_count(PREEMPT_ACTIVE); schedule(); sub_preempt_count(PREEMPT_ACTIVE); barrier(); } while (unlikely(test_thread_flag(TIF_NEED_RESCHED))); } #########################################################################################; 内核中的执行路径主要有: 1 用户进程的内核态,此时有进程context,主要是代表进程在执行系统调用等。 还包括,内核中自己的进程,如 ksoftirqd 等等 2 中断或者异常或者自陷等,从概念上说,此时没有进程context,不能进行context switch。 3 bottom_half,从概念上说,此时也没有进程context。 4 同时,相同的执行路径还可能在其他的CPU上运行。 Linux2.6中网络代码中的preempt_enable/disable移到softirqd调用的地方原因是这样的. 一、部分softirq是isr处理之后调用的, 对于这部分代码,由于是在底半处理中运行,必须是是在运行进程系统调用之前返回的. 所以实际上preempt_disable(); preempt_enable();代码对于他们来说是没有意义的. 二、部分softirq是在ksoftirqd的内核线程运行的, 因为这个相当于运行在进程的内核空间,由于软中断都是对中断上半部的继续, 所以这些工作都需要尽快的完成.所以在softirqd运行的时候,禁止了preempt, 这样就可以保证softirq运行完之后才会调度下一个进程,因为softirq里面的所有函数都不会睡眠.