博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
进程管理之schedule()
阅读量:2458 次
发布时间:2019-05-11

本文共 16580 字,大约阅读时间需要 55 分钟。

  1. /* 
  2.  * schedule() is the main scheduler function. 
  3.  */  
  4. asmlinkage void __sched schedule(void)  
  5. {  
  6.     struct task_struct *prev, *next;  
  7.     unsigned long *switch_count;  
  8.     struct rq *rq;  
  9.     int cpu;  
  10.   
  11. need_resched:  
  12.     preempt_disable(); //禁止抢占   
  13.     cpu = smp_processor_id();//获取当前CPU的ID   
  14.     rq = cpu_rq(cpu);//获取当前CPU上的rq(run queue)队列   
  15.     rcu_note_context_switch(cpu);//??????   
  16.     prev = rq->curr; //当前进程就是需要被切换出去的进程   
  17.   
  18.     schedule_debug(prev);//time debugging checks and statistics   
  19.   
  20.     if (sched_feat(HRTICK))  
  21.         hrtick_clear(rq);  
  22.   
  23.     raw_spin_lock_irq(&rq->lock);  
  24.   
  25.     switch_count = &prev->nivcsw;  
  26.     if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) { //如果当前进程处于stopped状态,并且处于可抢占状态???   
  27.         if (unlikely(signal_pending_state(prev->state, prev))) {
    //如果没有待处理的信号???   
  28.             prev->state = TASK_RUNNING;                     //则将当前进程的状态设置为RUNNING   
  29.         } else {                                                //如果有待处理的信号   
  30.             deactivate_task(rq, prev, DEQUEUE_SLEEP);       //将当前进程从运行队列中移走   
  31.             prev->on_rq = 0;  //表明当前进程已经不在rq队列中了。   
  32.   
  33.             /* 
  34.              * If a worker went to sleep, notify and ask workqueue 
  35.              * whether it wants to wake up a task to maintain 
  36.              * concurrency. 
  37.              */  
  38.             if (prev->flags & PF_WQ_WORKER) {  
  39.                 struct task_struct *to_wakeup;  
  40.   
  41.                 to_wakeup = wq_worker_sleeping(prev, cpu);  
  42.                 if (to_wakeup)  
  43.                     try_to_wake_up_local(to_wakeup);  
  44.             }  
  45.   
  46.             /* 
  47.              * If we are going to sleep and we have plugged IO 
  48.              * queued, make sure to submit it to avoid deadlocks. 
  49.              */  
  50.             if (blk_needs_flush_plug(prev)) { //冲刷IO   
  51.                 raw_spin_unlock(&rq->lock);  
  52.                 blk_schedule_flush_plug(prev);  
  53.                 raw_spin_lock(&rq->lock);  
  54.             }  
  55.         }  
  56.         switch_count = &prev->nvcsw;  
  57.     }  
  58.   
  59.     pre_schedule(rq, prev); //如果非SMP或者非实时任务调度,则为空操作;否则,将当前rt任务放回,并挑选另一个优先级更高的rt任务放到相对空闲的cpu中运行。   
  60.   
  61.     if (unlikely(!rq->nr_running))//当然,这个也是针对SMP而言的,因为在上一步过后,可能出现有的cpu空转的现象,所以要在smp间调度   
  62.         idle_balance(cpu, rq);   
  63.   
  64.     put_prev_task(rq, prev);//主要是更新待切换进程的运行时间等参数。   
  65.     next = pick_next_task(rq); //寻找下一个合适的进程以将其调度进来,这是核心函数,我们会在另外一篇博文中详述。   
  66.     clear_tsk_need_resched(prev);//TIF_NEED_RESCHED indicates that the process should be or would like to be replaced with another process by the scheduler.   
  67.   
  68.     rq->skip_clock_update = 0; //?????   
  69.   
  70.     if (likely(prev != next)) { //如果下一个进程与当前进程不是是一个进程   
  71.         rq->nr_switches++; //????   
  72.         rq->curr = next;//将运行队列的当前进程指向被选中的进程   
  73.         ++*switch_count;  
  74.   
  75.         context_switch(rq, prev, next); /* unlocks the rq */ /*切换进程上下文,无疑这是个核心函数,我们将在另外的博文中详细讲解*/  
  76.         /* 
  77.          * The context switch have flipped(翻转) the stack from under us 
  78.          * and restored the local variables which were saved when 
  79.          * this task called schedule() in the past. prev == current 
  80.          * is still correct, but it can be moved to another cpu/rq. 
  81.          */  
  82.         cpu = smp_processor_id();  
  83.         rq = cpu_rq(cpu);  
  84.     } else  
  85.         raw_spin_unlock_irq(&rq->lock);  
  86.   
  87.     post_schedule(rq); //和pre_schedule一样,只有在SMP中调度rt任务时才会被实际的执行,否则为空;同时与pre_schedule的作用类似,   
  88.                              
  89. /*这个函数的作用是: 
  90.  * If the current CPU has more than one RT task, see if the non 
  91.  * running task can migrate over to a CPU that is running a task 
  92.  * of lesser priority. 
  93.  */  
  94.     preempt_enable_no_resched();  
  95.     if (need_resched()) //如果被切换进来的进程仍需要被调度,则返回至need_resched,重新调度。   
  96.         goto need_resched;  
  97. }  

 

  1. /* 
  2.  * Pick up the highest-prio task: 
  3.  */  
  4. static inline struct task_struct *  
  5. pick_next_task(struct rq *rq)  
  6. {  
  7.     const struct sched_class *class;  
  8.     struct task_struct *p;  
  9.   
  10.     /* 
  11.      * Optimization: we know that if all tasks are in 
  12.      * the fair class we can call that function directly: 
  13.      */  
  14.     if (likely(rq->nr_running == rq->cfs.nr_running)) { //如果nr_running==cfs.nr_running,则说明当前rq队列中是没有rt任务的,   
  15.                                                             //rt任务不在cfs队列中,它的优先级的设置和cfs不一样。   
  16.         p = fair_sched_class.pick_next_task(rq); //在cfs队列中挑选即将被切换进去的进程,核心函数,我们下文会详细讲解。   
  17.         if (likely(p))  
  18.             return p;  
  19.     }  
  20.   
  21.     for_each_class(class) {   
  22.         p = class->pick_next_task(rq);  
  23.         if (p)  
  24.             return p;  
  25.     }  
  26.   
  27.     BUG(); /* the idle class will always have a runnable task */  
  28. }  
/* * Pick up the highest-prio task: */static inline struct task_struct *pick_next_task(struct rq *rq){	const struct sched_class *class;	struct task_struct *p;	/*	 * Optimization: we know that if all tasks are in	 * the fair class we can call that function directly:	 */	if (likely(rq->nr_running == rq->cfs.nr_running)) { //如果nr_running==cfs.nr_running,则说明当前rq队列中是没有rt任务的,                                                            //rt任务不在cfs队列中,它的优先级的设置和cfs不一样。		p = fair_sched_class.pick_next_task(rq); //在cfs队列中挑选即将被切换进去的进程,核心函数,我们下文会详细讲解。		if (likely(p))			return p;	}	for_each_class(class) { 		p = class->pick_next_task(rq);		if (p)			return p;	}	BUG(); /* the idle class will always have a runnable task */}

当CPU中没有rt任务时,那么从cfs队列中挑选合适的进程以待切换进CPU,因为是cfs调度类,所以最终调度的函数是pick_next_task_fair:

[cpp]
  1. static struct task_struct *pick_next_task_fair(struct rq *rq)  
  2. {  
  3.     struct task_struct *p;  
  4.     struct cfs_rq *cfs_rq = &rq->cfs;  
  5.     struct sched_entity *se;  
  6.   
  7.     if (!cfs_rq->nr_running)   
  8.         return NULL;  
  9.   
  10.     do {  
  11.         se = pick_next_entity(cfs_rq); //挑选下一个调度实体,详解见下文   
  12.         set_next_entity(cfs_rq, se); //   
  13.         cfs_rq = group_cfs_rq(se);   
  14.     } while (cfs_rq);  <SPAN style="COLOR: #ff0000">//如果被调度的进程仍属于当前组,那么选取下一个可能被调度的任务,以保证组间调度的公平性???</SPAN>   
  15.   
  16.     p = task_of(se);  
  17.     hrtick_start_fair(rq, p);  
  18.   
  19.     return p;  
  20. }  
static struct task_struct *pick_next_task_fair(struct rq *rq){	struct task_struct *p;	struct cfs_rq *cfs_rq = &rq->cfs;	struct sched_entity *se;	if (!cfs_rq->nr_running) 		return NULL;	do {		se = pick_next_entity(cfs_rq); //挑选下一个调度实体,详解见下文		set_next_entity(cfs_rq, se); //		cfs_rq = group_cfs_rq(se); 	} while (cfs_rq);  //如果被调度的进程仍属于当前组,那么选取下一个可能被调度的任务,以保证组间调度的公平性???	p = task_of(se);	hrtick_start_fair(rq, p);	return p;}
来看一下pick_next_entity:

[cpp]
  1. /* 
  2.  * Pick the next process, keeping these things in mind, in this order: 
  3.  * 1) keep things fair between processes/task groups                    1. 首先要确保任务组之间的公平,这也是设置组的原因之一; 
  4.  * 2) pick the "next" process, since someone really wants that to run   2. 其次,挑选下一个合适的(优先级比较高的)进程,因为它确实需要马上运行; 
  5.  * 3) pick the "last" process, for cache locality                       3. 如果没有找到条件2中的进程,那么为了保持良好的局部性,则选中上一次执行的进程; 
  6.  * 4) do not run the "skip" process, if something else is available     4. 只要有任务存在,就不要让CPU空转,也就是让CPU运行idle进程。 
  7.  */  
  8. static struct sched_entity *pick_next_entity(struct cfs_rq *cfs_rq)  
  9. {  
  10.     struct sched_entity *se = __pick_first_entity(cfs_rq); //摘取红黑树最左边的进程   
  11.     struct sched_entity *left = se;  
  12.   
  13.     /* 
  14.      * Avoid running the skip buddy, if running something else can 
  15.      * be done without getting too unfair. 
  16.      */  
  17.     if (cfs_rq->skip == se) { //如果是被取到的进程自动放弃运行权利   
  18.         /* 
  19.          *cfs_rq队列中的*skip指向暂时不需要被调度执行的进程,这样的进程一般通过sched_yield() 
  20.          *(在CFS中是通过yield_task_fair)放弃执行权的,同时在sched_yield设置skip之前,总是将上一个被设置为skip的进程清除掉,以防止放弃 
  21.          *运行权利的进程永远得不到调度。 
  22.          */  
  23.                 struct sched_entity *second = __pick_next_entity(se); //摘取红黑树上第二左的进程节点   
  24.         if (second && wakeup_preempt_entity(second, left) < 1) //left应该抢占second么?这个函数的具体实现会在下文详述。   
  25.             se = second; //left不会抢占second,则se = second.   
  26.     }  
  27.   
  28.     /* 
  29.      * Prefer last buddy, try to return the CPU to a preempted task. 
  30.      */  
  31.     if (cfs_rq->last && wakeup_preempt_entity(cfs_rq->last, left) < 1)  
  32.         //如果上一次执行的进程尚在cfs_rq队列中,并且left不能抢占它,这里我们应该能够想到为什么内核线程的active_mm要借用   
  33.         //上一个进程的active_mm。这样的话上一个activ_mm就不用从tlb中清洗掉,而下一次调度的时候有可能重新调度到该进程,增加了tlb的命中率。   
  34.                 se = cfs_rq->last;   
  35.   
  36.     /* 
  37.      * Someone really wants this to run. If it's not unfair, run it. 
  38.      */  
  39.     if (cfs_rq->next && wakeup_preempt_entity(cfs_rq->next, left) < 1)//如果cfs_rq中指向下一个即将被调度进来的进程的指针不为空,并且left不能抢占它    
  40.         se = cfs_rq->next;  
  41.   
  42.     clear_buddies(cfs_rq, se); //清空last, next, skip指针   
  43.   
  44.     return se;  
  45. }  
/* * Pick the next process, keeping these things in mind, in this order: * 1) keep things fair between processes/task groups                    1. 首先要确保任务组之间的公平,这也是设置组的原因之一; * 2) pick the "next" process, since someone really wants that to run   2. 其次,挑选下一个合适的(优先级比较高的)进程,因为它确实需要马上运行; * 3) pick the "last" process, for cache locality                       3. 如果没有找到条件2中的进程,那么为了保持良好的局部性,则选中上一次执行的进程; * 4) do not run the "skip" process, if something else is available     4. 只要有任务存在,就不要让CPU空转,也就是让CPU运行idle进程。 */static struct sched_entity *pick_next_entity(struct cfs_rq *cfs_rq){	struct sched_entity *se = __pick_first_entity(cfs_rq); //摘取红黑树最左边的进程	struct sched_entity *left = se;	/*	 * Avoid running the skip buddy, if running something else can	 * be done without getting too unfair.	 */	if (cfs_rq->skip == se) { //如果是被取到的进程自动放弃运行权利        /*         *cfs_rq队列中的*skip指向暂时不需要被调度执行的进程,这样的进程一般通过sched_yield()         *(在CFS中是通过yield_task_fair)放弃执行权的,同时在sched_yield设置skip之前,总是将上一个被设置为skip的进程清除掉,以防止放弃         *运行权利的进程永远得不到调度。         */                struct sched_entity *second = __pick_next_entity(se); //摘取红黑树上第二左的进程节点		if (second && wakeup_preempt_entity(second, left) < 1) //left应该抢占second么?这个函数的具体实现会在下文详述。			se = second; //left不会抢占second,则se = second.	}	/*	 * Prefer last buddy, try to return the CPU to a preempted task.	 */	if (cfs_rq->last && wakeup_preempt_entity(cfs_rq->last, left) < 1)        //如果上一次执行的进程尚在cfs_rq队列中,并且left不能抢占它,这里我们应该能够想到为什么内核线程的active_mm要借用        //上一个进程的active_mm。这样的话上一个activ_mm就不用从tlb中清洗掉,而下一次调度的时候有可能重新调度到该进程,增加了tlb的命中率。                se = cfs_rq->last; 	/*	 * Someone really wants this to run. If it's not unfair, run it.	 */	if (cfs_rq->next && wakeup_preempt_entity(cfs_rq->next, left) < 1)//如果cfs_rq中指向下一个即将被调度进来的进程的指针不为空,并且left不能抢占它 		se = cfs_rq->next;	clear_buddies(cfs_rq, se); //清空last, next, skip指针	return se;}

我们首先要关注的是 pick_next_entity中调用的第一个函数__pick_first_entity:

[cpp]
  1. static struct sched_entity *__pick_first_entity(struct cfs_rq *cfs_rq)  
  2. {  
  3.     struct rb_node *left = cfs_rq->rb_leftmost;  //挑选cfs队列红黑树中最左的元素   
  4.   
  5.     if (!left)  
  6.         return NULL;  
  7.   
  8.     return rb_entry(left, struct sched_entity, run_node);  
  9. }  
static struct sched_entity *__pick_first_entity(struct cfs_rq *cfs_rq){	struct rb_node *left = cfs_rq->rb_leftmost;  //挑选cfs队列红黑树中最左的元素	if (!left)		return NULL;	return rb_entry(left, struct sched_entity, run_node);}

                                   现在,有必要对CFS调度机制有一个深入的了解了。推荐参考网站:http://blog.csdn.net/peimichael/article/details/5218335或者《独辟蹊径》一书。

接着,来看一下在pick_next_entity中被反复用到的一个函数:

[cpp]
  1. /* 
  2.  * Should 'se' preempt 'curr'. 
  3.  * 
  4.  *             |s1 
  5.  *        |s2 
  6.  *   |s3 
  7.  *         g 
  8.  *      |<--->|c 
  9.  * 
  10.  *  w(c, s1) = -1 
  11.  *  w(c, s2) =  0 
  12.  *  w(c, s3) =  1 
  13.  * 
  14.  */  
  15. static int  
  16. wakeup_preempt_entity(struct sched_entity *curr, struct sched_entity *se)  
  17. {  
  18.     s64 gran, vdiff = curr->vruntime - se->vruntime; //curr和se之间虚拟时钟的差别   
  19.   
  20.     if (vdiff <= 0)     //如果curr的虚拟时钟小于se的虚拟时钟   
  21.         return -1;  //则se不能抢占curr   
  22.   
  23.     gran = wakeup_gran(curr, se); //计算粒度,详述见下文   
  24.     if (vdiff > gran)  //如果vdiff大于粒度,则允许se抢占curr   
  25.         return 1;  
  26.   
  27.     return 0;  
  28. }  
/* * Should 'se' preempt 'curr'. * *             |s1 *        |s2 *   |s3 *         g *      |<--->|c * *  w(c, s1) = -1 *  w(c, s2) =  0 *  w(c, s3) =  1 * */static intwakeup_preempt_entity(struct sched_entity *curr, struct sched_entity *se){	s64 gran, vdiff = curr->vruntime - se->vruntime; //curr和se之间虚拟时钟的差别	if (vdiff <= 0)     //如果curr的虚拟时钟小于se的虚拟时钟		return -1;  //则se不能抢占curr	gran = wakeup_gran(curr, se); //计算粒度,详述见下文	if (vdiff > gran)  //如果vdiff大于粒度,则允许se抢占curr		return 1;	return 0;}

这个函数返回-1表示新进程vruntime大于当前进程,当然不能抢占,返回0表示虽然新进程vruntime比当前进程小,但是没有小到调度粒度,一般也不能抢占。返回1表示新进程vruntime比当前进程小的超过了调度粒度,可以抢占。

调度粒度是什么概念呢?进程调度的时候每次都简单选择vruntime最小的进程调度,其实也不完全是这样。
假设进程A和B的vruntime很接近,那么A先运行了一个tick,vruntime比B大了,B又运行一个tick,vruntime又比A大了,又切换到A,这样就会在AB间频繁切换,对性能影响很大,因此如果当前进程的时间没有用完,就只有当有进程的vruntime比当前进程小超过调度粒度时,才能进行进程切换。
函数上面注释中那个图就是这个意思,我们看下:
横坐标表示vruntime,s1 s2 s3分别表示新进程,c表示当前进程,g表示调度粒度。
s3肯定能抢占c;而s1不可能抢占c。
s2虽然vruntime比c小,但是在调度粒度之内,能否抢占要看情况,像现在这种状况就不能抢占。

(摘自:http://blog.csdn.net/peimichael/article/details/5218335)

在选中了下一个将被调度执行的进程之后,回到pick_next_task_fair中,执行set_next_entity():

[cpp]
  1. static void  
  2. set_next_entity(struct cfs_rq *cfs_rq, struct sched_entity *se)  
  3. {  
  4.     /* 'current' is not kept within the tree. */  
  5.     if (se->on_rq) { //如果se尚在rq队列上   
  6.         /* 
  7.          * Any task has to be enqueued before it get to execute on 
  8.          * a CPU. So account for the time it spent waiting on the 
  9.          * runqueue. 
  10.          */  
  11.         update_stats_wait_end(cfs_rq, se);  
  12.         __dequeue_entity(cfs_rq, se); //将se从rq队列中删除   
  13.                 /* 
  14.                  *static void __dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se) 
  15.                  *{
     
  16.                  *    if (cfs_rq->rb_leftmost == &se->run_node) {
     
  17.                  *        struct rb_node *next_node; 
  18.  
  19.                  *        next_node = rb_next(&se->run_node); 
  20.                  *        cfs_rq->rb_leftmost = next_node; //挑选下一个leftmost 
  21.                  *    } 
  22.                   
  23.  
  24.                  *     rb_erase(&se->run_node, &cfs_rq->tasks_timeline); //调整rb树 
  25.                  *} 
  26.                  */  
  27.     }  
  28.   
  29.     update_stats_curr_start(cfs_rq, se); //We are picking a new current task.更新sched_entity中的exec_start字段为当前clock_task.   
  30.     cfs_rq->curr = se; //将se设置为curr进程   
  31. #ifdef CONFIG_SCHEDSTATS   
  32.     /* 
  33.      * Track our maximum slice length, if the CPU's load is at 
  34.      * least twice that of our own weight (i.e. dont track it 
  35.      * when there are only lesser-weight tasks around): 
  36.      */  
  37.     if (rq_of(cfs_rq)->load.weight >= 2*se->load.weight) {  
  38.         se->statistics.slice_max = max(se->statistics.slice_max,  
  39.             se->sum_exec_runtime - se->prev_sum_exec_runtime);  
  40.     }  
  41. #endif   
  42.     se->prev_sum_exec_runtime = se->sum_exec_runtime; //更新task上一次投入运行的从时间。   
  43. }  
static voidset_next_entity(struct cfs_rq *cfs_rq, struct sched_entity *se){	/* 'current' is not kept within the tree. */	if (se->on_rq) { //如果se尚在rq队列上		/*		 * Any task has to be enqueued before it get to execute on		 * a CPU. So account for the time it spent waiting on the		 * runqueue.		 */		update_stats_wait_end(cfs_rq, se);		__dequeue_entity(cfs_rq, se); //将se从rq队列中删除                /*                 *static void __dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se)                 *{                 *    if (cfs_rq->rb_leftmost == &se->run_node) {                 *        struct rb_node *next_node;                 *        next_node = rb_next(&se->run_node);                 *        cfs_rq->rb_leftmost = next_node; //挑选下一个leftmost                 *    }                                  *     rb_erase(&se->run_node, &cfs_rq->tasks_timeline); //调整rb树                 *}                 */	}	update_stats_curr_start(cfs_rq, se); //We are picking a new current task.更新sched_entity中的exec_start字段为当前clock_task.	cfs_rq->curr = se; //将se设置为curr进程#ifdef CONFIG_SCHEDSTATS	/*	 * Track our maximum slice length, if the CPU's load is at	 * least twice that of our own weight (i.e. dont track it	 * when there are only lesser-weight tasks around):	 */	if (rq_of(cfs_rq)->load.weight >= 2*se->load.weight) {		se->statistics.slice_max = max(se->statistics.slice_max,			se->sum_exec_runtime - se->prev_sum_exec_runtime);	}#endif	se->prev_sum_exec_runtime = se->sum_exec_runtime; //更新task上一次投入运行的从时间。}

以上,我们讨论了公平调度的情况,回到pick_next_task中,如果rq队列中进程的数量不等于CFS中进程的数量,那么说明有其它类型的任务需要被优先调度。

[cpp]
  1. for_each_class(class) {   
  2.     p = class->pick_next_task(rq);  
  3.     if (p)  
  4.         return p;  
  5. }  
for_each_class(class) { 		p = class->pick_next_task(rq);		if (p)			return p;	}

对于for_each_class:

[cpp]
  1. #define sched_class_highest (&stop_sched_class)   
  2. #define for_each_class(class) \   
  3.    for (class = sched_class_highest; classclass = class->next)  
#define sched_class_highest (&stop_sched_class)#define for_each_class(class) \   for (class = sched_class_highest; class; class = class->next)
我们可以得到这样的结论,不同类型的任务被调度的顺序是:

stop_sched_class     ---->     rt_sched_class    ---->    fair_sched_class    ---->    idle_sched_class    ---->    NULL

首先看一下stop_sched_class选取下一个进程的过程:

[cpp]
  1. static struct task_struct *pick_next_task_stop(struct rq *rq)  
  2. {  
  3.     struct task_struct *stop = rq->stop;  //直接将rq队列中的stop指向的进程作为即将被调度进来的进程。   
  4.   
  5.     if (stop && stop->on_rq)  
  6.         return stop;  
  7.   
  8.     return NULL;  
  9. }  
static struct task_struct *pick_next_task_stop(struct rq *rq){	struct task_struct *stop = rq->stop;  //直接将rq队列中的stop指向的进程作为即将被调度进来的进程。	if (stop && stop->on_rq)		return stop;	return NULL;}
然后,看rt_sched_class挑选下一个进程的过程:

[cpp]
  1. static struct task_struct *pick_next_task_rt(struct rq *rq)  
  2. {  
  3.     struct task_struct *p = _pick_next_task_rt(rq);  
  4.   
  5.     /* The running task is never eligible for pushing */  
  6.     if (p)  
  7.         dequeue_pushable_task(rq, p);  
  8.   
  9. #ifdef CONFIG_SMP   
  10.     /* 
  11.      * We detect this state here so that we can avoid taking the RQ 
  12.      * lock again later if there is no need to push 
  13.      */  
  14.     rq->post_schedule = has_pushable_tasks(rq);  
  15. #endif   
  16.   
  17.     return p;  
  18. }  
static struct task_struct *pick_next_task_rt(struct rq *rq){	struct task_struct *p = _pick_next_task_rt(rq);	/* The running task is never eligible for pushing */	if (p)		dequeue_pushable_task(rq, p);#ifdef CONFIG_SMP	/*	 * We detect this state here so that we can avoid taking the RQ	 * lock again later if there is no need to push	 */	rq->post_schedule = has_pushable_tasks(rq);#endif	return p;}
其中的关键操作自然是_pick_next_task_rt:

[cpp]
  1. static struct sched_rt_entity *pick_next_rt_entity(struct rq *rq,  
  2.                            struct rt_rq *rt_rq)  
  3. {  
  4.     struct rt_prio_array *array = &rt_rq->active;  
  5.     struct sched_rt_entity *next = NULL;  
  6.     struct list_head *queue;  
  7.     int idx;  
  8.   
  9.     idx = sched_find_first_bit(array->bitmap); //找到active->bitmap中第一个置位的比特位,即高优先级。   
  10.     BUG_ON(idx >= MAX_RT_PRIO);  
  11.   
  12.     queue = array->queue + idx;  
  13.     next = list_entry(queue->next, struct sched_rt_entity, run_list);  
  14.   
  15.     return next;  
  16. }  
static struct sched_rt_entity *pick_next_rt_entity(struct rq *rq,						   struct rt_rq *rt_rq){	struct rt_prio_array *array = &rt_rq->active;	struct sched_rt_entity *next = NULL;	struct list_head *queue;	int idx;	idx = sched_find_first_bit(array->bitmap); //找到active->bitmap中第一个置位的比特位,即高优先级。	BUG_ON(idx >= MAX_RT_PRIO);	queue = array->queue + idx;	next = list_entry(queue->next, struct sched_rt_entity, run_list);	return next;}

实时进程的调度用的是和CFS不同的调度方法,不过想比于CFS,它更简单。详细的可以参看《深入Linux内核架构》P117.

 

你可能感兴趣的文章
自动化脚本自动化执行_5个用于自动化基本社区管理任务的脚本
查看>>
helm 和kubectl_适用于初学者的基本kubectl和Helm命令
查看>>
linux防火墙_Linux防火墙入门
查看>>
matlab 替代品_MATLAB的4种开源替代品
查看>>
minecraft1.5_Minecraft的5种开源替代品
查看>>
微软 开源 控制台_使用此开源窗口环境一次运行多个控制台
查看>>
tmux 嵌套 tmux_使用tmux和kubectl的功能对Kubernetes进行故障排除
查看>>
如何创建工件坐标系_自动创建研究工件
查看>>
针对CI / CD管道和Windows用户的提示以及更多Ansible新闻
查看>>
ansible剧本如何写_我学过的3课:写Ansible剧本
查看>>
bash 脚本部署lmnp_使用Bash自动化Helm部署
查看>>
linux 中移动文件_如何在Linux中移动文件
查看>>
ansible 模块_您需要知道的10个Ansible模块
查看>>
无处不在_Kubernetes几乎无处不在,正在使用Java以及更多的行业趋势
查看>>
ansible 中文文档_浏览Ansible文档,自动执行补丁,虚拟化以及更多新闻
查看>>
人脸关键点 开源数据_谦虚是开源成功的关键,Kubernetes安全斗争以及更多行业趋势...
查看>>
markdown_Markdown初学者备忘单
查看>>
devops失败的原因_失败是无可指责的DevOps的功能
查看>>
开源项目演示_3种开源工具可让您的演示文稿流行
查看>>
rust编程语言_Mozilla的Rust编程语言处于关键阶段
查看>>