进程抢占-触发抢占的时机和执行抢占的时机

一、触发抢占的时机:

  1. 周期性时钟中断:检查时间片是否用完,如果用完触发抢占

          scheduler_tick

            curr->sched_class->task_tick(rq, curr, 0);

  1. 唤醒进程的时候,如果优先级高于cpu上的当前进程,就会发出抢占:try_to_wake_up(),最终会调用check_preempt_curr()检查是否触发抢占;
  2. 新进程创建的时候,如果新进程的优先级高于CPU上的当前进程,会触发抢占:sched_fork()->task_fork方法触发抢占;
  3. 修改nice值的时候:如果进程修改nice值导致优先级高于CPU上的当前进程,就会触发抢占:set_user_nice();
  4. 进行负载均衡的时候:在SMP系统上,进程调度器尽量使各个CPU之间的的负载保持均衡,而负载均衡操作可能会需要触发抢占;不同调度类有不同的负载均衡算法,涉及的核心代码也不一样,比如CFS类在load_balance()中触发抢占:

Load_balance->resched_cpu      

二、执行抢占的时机:

 2.1用户态抢占时机

(1)从系统调用返回用户态时

源文件:arch/x86/kernel/entry_64.S

   sysret_careful:

   bt $TIF_NEED_RESCHED,%edx

   jnc sysret_signal

   TRACE_IRQS_ON

   ENABLE_INTERRUPTS(CLBR_NONE)

   pushq_cfi %rdi

   SCHEDULE_USER//会调用schedule

   popq_cfi %rdi

   jmp sysret_check

(2)从中断(异常)返回用户态时:

源文件:arch/x86/kernel/entry_64.S

retint_careful:

   CFI_RESTORE_STATE

   bt    $TIF_NEED_RESCHED,%edx

   jnc   retint_signal

   TRACE_IRQS_ON

   ENABLE_INTERRUPTS(CLBR_NONE)

   pushq_cfi %rdi

   SCHEDULE_USER//会调用schedule

   popq_cfi %rdi

   GET_THREAD_INFO(%rcx)

   DISABLE_INTERRUPTS(CLBR_NONE)

   TRACE_IRQS_OFF

            jmp retint_check

2.2 内核态抢占时机:Linux在2.6版本之后就支持内核抢占了,但是请注意,具体取决于内核编译时的选项:

    CONFIG_PREEMPT_NONE=y

    不允许内核抢占。这是SLES的默认选项。

    CONFIG_PREEMPT_VOLUNTARY=y

    在一些耗时较长的内核代码中主动调用cond_resched()让出CPU。这是RHEL的默认选项。

    CONFIG_PREEMPT=y

    允许完全内核抢占。

在 CONFIG_PREEMPT=y 的前提下,内核态抢占的时机是:

(1)中断处理程序返回内核空间之前会检查TIF_NEED_RESCHED标志,如果置位则调用preempt_schedule_irq()执行抢占。preempt_schedule_irq()是对schedule()的包装

   ENTRY(retint_kernel)

   cmpl $0,PER_CPU_VAR(__preempt_count)

   jnz  retint_restore_args

   bt   $9,EFLAGS-ARGOFFSET(%rsp)       /* interrupts off? */

   jnc  retint_restore_args

   call preempt_schedule_irq//产生调度时机

   jmp exit_intr

(2)当内核从non-preemptible(禁止抢占)状态变成preemptible(允许抢占)的时候

在preempt_enable()中,会最终调用 preempt_schedule 来执行抢占。preempt_schedule()是对schedule()的包装。

三、参考链接

http://linuxperf.com/?p=211

你可能感兴趣的:(Linux进程调度与管理)