linux进程调度

schedule调度流程

      schedule就是主调度器的函数, 在内核中如果要将CPU分配给与当前活动进程不同的另一个进程, 都会直接调用主调度器函数schedule, 

该函数完成如下工作:

  1. 确定当前就绪队列, 并在保存一个指向当前(仍然)活动进程的task_struct指针。
  2. 检查死锁, 关闭内核抢占后调用__schedule完成内核调度。

  3. 恢复内核抢占, 然后检查当前进程是否设置了重调度标志TLF_NEDD_RESCHED, 如果该进程被其他进程设置了TIF_NEED_RESCHED标志, 则函数重新执行进行调度。

do {
        preempt_disable();                                  /*  关闭内核抢占  */
        __schedule(false);                                  /*  完成调度  */
        sched_preempt_enable_no_resched();                  /*  开启内核抢占  */
    } while (need_resched());   /*  如果该进程被其他进程设置了TIF_NEED_RESCHED标志,则函数重新执行进行调度    */

__schedule如何完成内核抢占

       查看__schedule 函数实现,主要做如下工作:

  1. 完成一些必要的检查, 并设置进程状态, 处理进程所在的就绪队列。

  2. 调度全局的pick_next_task选择抢占的进程。

    • 如果当前cpu上所有的进程都是cfs调度的普通非实时进程, 则直接用cfs调度, 如果无程序可调度则调度idle进程。

    • 否则从优先级最高的调度器类sched_class_highest(目前是stop_sched_class)开始依次遍历所有调度器类的pick_next_task函数, 选择最优的那个进程执行。

  3. context_switch完成进程上下文切换。

    • 调用switch_mm(), 把虚拟内存从一个进程映射切换到新进程中。

    • 调用switch_to(),从上一个进程的处理器状态切换到新进程的处理器状态。这包括保存、恢复栈信息和寄存器信息。

调度的内核抢占和用户抢占

      内核在完成调度的过程中总是先关闭内核抢占, 等待内核完成调度的工作后, 再把内核抢占开启, 如果在内核完成调度器过程中, 这时候如果发生了内核抢占,我们的调度会被中断, 而调度却还没有完成, 这样会丢失我们调度的信息。

      在调度完成后, 内核会去判断need_resched条件, 如果这个时候为真, 内核会重新进程一次调度,此次调度由于发生在内核态因此仍然是一次内核抢占need_resched条件其实是判断need_resched标识TIF_NEED_RESCHED的值,内核在thread_info的flag中设置了一个标识来标志进程是否需要重新调度, 即重新调度need_resched标识TIF_NEED_RESCHED,内核在即将返回用户空间时会检查标识TIF_NEED_RESCHED标志进程是否需要重新调度,如果设置了,就会发生调度, 这被称为用户抢占,

      而内核抢占是通过自旋锁preempt_count实现的, 同样当内核可以进行内核抢占的时候(比如从中断处理程序返回内核空间或内核中的进程被堵塞的时候),内核会检查preempt_count和TIF_NEED_RESCHED标志,如果进程设置了 TIF_NEED_RESCHED标志,并且preempt_count为0,则发生内核抢占。

原文地址:https://www.cnblogs.com/jerry116/p/8811253.html