linux2.6.29 CFS调度详细分析(二)

上次主要讲了 CFS 调度的基本原理,并且没有分析有唤醒和进程迁移时候的调度流程,所以本文主要 从内核中几个重要的调度点来详细的分析一下调度的基本流程,主要以流程图的形式给出。
    内核中主要有以下几个重要的切入点 :
    (1)tick 相关,即时钟中断

这就是上篇文章中讲的每次中断中,更 新 vruntime 的整个过程,可以理解为是在中断的上半部分做的,很显然我们会想到前一篇文章中 讲到的检查 TIF_NEED_SCHED 位并显示调用 schedule() 的地方就是中断的下半部分了,为了更好的理解我把中断的整个处理流程也做了个流程图 ( 以时钟中断为例 )

 

( 这些流程图很大的,如果看不清可以下载下来放大看,应该相当清晰 )

与中断相关的部分,我会在以后的文章里详细介绍,在这里就不具体接讲解了。

现在来总结一下这个切入点的整个过程:

当来一个时钟中断的时候,概括来说内核做了两个操作 ( 参见上图中 )

 

(1)do_timer(): 它主要来更新系统的时间

(2)update_progress_times(): 一方面去执行我们上面谈到的 scheduler_tick() ,另一方面他又去触发一个软中断

执行完上面几个步骤后,时钟中断就要返回了,因为其他的任务留给软中断做了。具体怎么做我会在中断一部分在分析。我们来看看中断返回时的一段 代码:

     call do_IRQ
     jmp ret_from_intr  # 执行完 do_IRQ 后跳到 ret_from_intr  

ret_from_intr:

     ..............
     cmpl $USER_RPL, %eax  # 判断返回用户态还是内核态
     jb resume_kernel  # not returning to v8086 or userspace

ENTRY(resume_userspace)
     ..............

 

     jne work_pending
     jmp restore_all

ENTRY(resume_kernel)
     ..........

need_resched:
     movl TI_flags(%ebp), %ecx # need_resched set ?
     testb $_TIF_NEED_RESCHED, %cl     # 检查 TIF_NEED_SCHED 有没有被置位
     jz restore_all
     testl $X86_EFLAGS_IF,PT_EFLAGS(%esp) # interrupts off (exception path) ?
     jz restore_all
     call preempt_schedule_irq          # 在这里显示调用 了 schedule()
 jmp need_resched

到这里我们已经跟踪了在时钟中断里面的一个完整的调度过程。

(2) 当前进程主动放弃 CPU

   既然是主动放弃,直接主动调用 schedule() 就行了,内核里面的很多地方都是这么做的,只要搜一下 schedule 的调用点就会看到。下面是从 schedule() 开始的一个调用

 

流程图:

 

总结一下执行的流程:

(1) 清除 TIF_NEED_SCHED

(2) 判断一下当前进程应该被继续设置为 TASK_RUNNING 状态,如果不是的话就把它从运行队列中删除

(3) 看一下当前运行队列是否还有可运行进 程,没有的话,调用 idle_balance(cpu, rq) 平衡负载

(4)把当前进程放回运行队列,并设置当前 进程的指针为空。

(5)选择下一个进程作为当前进程

(6)判断如果当前进程不等于选择的进程的 话,就进行进程切换,执行swtich_to,这部分将在下次讲解。

 

先说这么多,因为有很多细节我也不怎么理解,下面提出几个问题,希望能和大家一块讨论:

(1)schedule()函数中的这段代码及其展开

if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)){
  /*prev->state >0 即当前进程处在stopped状态下,而且可抢占的情况下*/
  if (unlikely(signal_pending_state(prev->state, prev)))
   prev->state = TASK_RUNNING;/**/
  else
   deactivate_task(rq, prev, 1) ;/*从红黑树中删除prev*/
  switch_count = &prev->nvcsw;

 

这部分的意思是如果当前进程还需要呆在运行队列中就设置其prev->state = TASK_RUNNING,否则把它从

红黑树中删除。

问题一:继续留在运行队列的那个判断条件具体是什么意思?

if (unlikely(signal_pending_state(prev->state, prev)))

猜测应该 是,TASK_INTERRUPTIBLE这种状态时,可随时接受信号来继续运行。

问题二: 如果不留在运行队列就要从红黑树种删除,跟踪删除函数最后到了dequeue_entity()的这段代码


  /*清除伙伴*/
 clear_buddies(cfs_rq, se);
       /*大部分情况下二者是相等的,不执行,
       因为当前进程一直就不在运行队列中
       问题的关键是什么情况下执行呢?*/
 if (se != cfs_rq->curr)
  __dequeue_entity(cfs_rq, se);

 

这个判断条件是什么意思?se不就是当前进程吗?什么情况下这个判断条件才能成立,最好能有个具体的例子。

我是这么想的,大部分情况下se = cfs_rq->curr,那么出队操作就不执行,因为当前进程是不在运行队列中的,这一点可以从新选择一个进程来运行的时候要先把它出列得到印 证,具体可见pick_next_task().

你可能感兴趣的:(linux,exception,timer,Path,Signal,returning)