本系列文章由张同浩编写,转载请注明出处:http://blog.csdn.net/muge0913/article/details/7563540
抢占时伴随着schedule()的执行。内核提供了一个TIF_NEED_RESCHED标志来表明是否要用schedule()调度一次。
根据抢占发生的时机分为用户抢占和内核抢占。用户抢占发生在内核即将返回到用户空间的时候。内核抢占发生在返回内核空间的时候。
1、用户抢占:内核在即将返回用户空间时检查进程是否设置了TIF_NEED_RESCHED标志,如果设置了,就会发生用户抢占。用户抢占发生的时机:从系统调用或中断处理程序返回用户空间的时候。
2、内核抢占:在不支持内核抢占的内核中,内核进程如果自己不主动停止,就会一直的运行下去。无法响应实时进程。抢占内核虽然牺牲了上下文切换的开销,但获得了更大的吞吐量和响应时间。2.6的内核添加了内核抢占,同时为了某些地方不被抢占,又添加了自旋锁。在进程的thread_info结构中添加了preempt_count该数值为0,当进程使用一个自旋锁时就加1,释放一个自旋锁时就减1.为0时表示内核可以抢占。
内核发生抢占的时机:1、从中断处理程序返回内核空间时,内核会检查preempt_count和TIF_NEED_RESCHED标志,如果进程设置了TIF_NEED_RESCHED标志,并且preempt_count为0,发生内核抢占。2、当内核再次用于可抢占性的时候,当进程所有的自旋锁都释放了,释放程序会检查TIF_NEED_RESCHED标志,如果设置了就会调用schedule。3、显示调用schedule时4、内核中的进程被堵塞的时候。
进程的切换:
从一个运行的进程转换到另一个可运行的进程时,称为进程切换(process switch)或上下文切换(context switch).
1、硬件上下文(hardware context)
2、任务状态段tss
3、硬件上下文的切换:linux内核中的硬件上下文不像intel原始设计的那样存放在tss中而是用一个特定了数据结构来存储。
4、context_switch
/* *context_switch - switch to the new MM and the new *thread's register state. */ static inline void context_switch(struct rq *rq, structtask_struct *prev, struct task_struct *next) { structmm_struct *mm, *oldmm; prepare_task_switch(rq,prev, next); trace_sched_switch(prev,next); /*获取即将调度的进程内存指针*/ mm =next->mm; /*保存当前要替换的进程使用的内存数据结构*/ oldmm= prev->active_mm; /* * For paravirt, this is coupled with an exitin switch_to to * combine the page table reload and the switchbackend into * one hypercall. */ arch_start_context_switch(prev); /*如果指向的即将调用的进程内存指针为null,则为内核线程*/ if(!mm) { next->active_mm= oldmm; /*内存描述结构使用计数加1*/ atomic_inc(&oldmm->mm_count); enter_lazy_tlb(oldmm,next); }else /*不是内核线程,切换页表*/ switch_mm(oldmm,mm, next); if(!prev->mm) { prev->active_mm= NULL; rq->prev_mm= oldmm; } /* * Since the runqueue lock will be released bythe next * task (which is an invalid locking op but inthe case * of the scheduler it's an obviousspecial-case), so we * do an early lockdep release here: */ #ifndef __ARCH_WANT_UNLOCKED_CTXSW spin_release(&rq->lock.dep_map,1, _THIS_IP_); #endif /*Here we just switch the register state and the stack. */ /*处理器状态切换*/ switch_to(prev,next, prev); barrier(); /* * this_rq must be evaluated again because prevmay have moved * CPUs since it called schedule(), thus the'rq' on its stack * frame will be invalid. */ finish_task_switch(this_rq(),prev); }