Definición


El planificador de Linux llamado Completely Fair implementa el que se denomina fair-share scheduling de una forma altamente eficiente y de forma escalable.

Para lograr la meta de ser eficiente, CFS, intenta gastar muy poco tiempo tomando decisiones de planificación, de dos formas, por su diseño y debido al uso inteligente de estructuras de datos para esa tarea

Modo de operación básico


Mientras que los planificadores tradicionales se basan alrededor del concepto de un time-slice fijo, CSF opera de forma un poco diferente. Su objetivo es sencillo, dividir de forma justa la Procesador entre todos los procesos que están compitiendo por ella.

Logra dividir la CPU mediante una simple técnica para contar llamada virtual runtime. A medida que un proceso se ejecuta este acumula vruntime. En el caso más básico cada vruntime de un proceso se incrementa con la misma tasa, en proporción al tiempo físico. Cuando una decisión de planificación ocurre, CFS seleccionará el proceso con menos vruntime para que sea el próximo en ser ejecutado.

Decisión de parar la ejecución

El punto clave aquí es que hay un punto de tensión entre performance y equitatividad

  • Si el CFS switchea de proceso en tiempos muy pequeños estará garantizado que todos los procesos se ejecuten a costa de perdida de performance. Esto es por demasiados context switch
  • Si el CFS switchea pocas veces, la performance del scheduler es buena pero el costo está puesto del lado de la equitatividad (fairness)

La forma en que CFS maneja esta tensión es mediante varios parámetros de control

  • sched_latency
    • Este valor determina por cuanto tiempo un proceso tiene que ejecutarse antes de considerar su switcheo. El valor típico de este parámetro es de , CFS divide este valor por el número de procesos () ejecutándose en la Procesador para determinar el time-slice de un proceso, y entonces se asegura que por ese periodo de tiempo, CFS va a ser completamente justo.
  • min_granularity
    • Para lidiar con los multiples context switch que puede producir un sched_latency muy chico, se introduce otro parámetro llamado min_grunularity, que normalmente se setea con el valor de . Entonces CFS nunca sedería el time-slice de un proceso por debajo de ese número, por ende con esto se asegura que no haya overhead por el context switch

CFS utiliza una interrupción periódica de tiempo, lo que significa que sólo puede tomar decisiones en periodos de tiempos fijos ()

Weighting

CFS tiene control sobre las prioridades de los procesos, de forma tal que los usuarios y administradores puedan asignar más Procesador a un determinado proceso. Esto se hace con un mecanismo clásico de UNIX llamado nivel de proceso nice, este valor va de a , con valor por defecto de . Con una característica un poco extraña, los valores positivos de nice implica una prioridad más baja, y los valores negativos de nice implican una prioridad más alta.

Estos pesos permite calcular efectivamente el time slice para cada proceso pero teniendo en cuenta las distintas prioridades para cada proceso

Con esto se debe generalizar el calculo de vruntime

Este se calcula tomando el tiempo de ejecución real que el ha acumulado () y lo escala de manera inversa según el peso del proceso

Un aspecto inteligente de la construcción de la tabla de pesos anterior es que la tabla conserva las proporciones de ratio de la Procesador cuando la diferencia en valores de nice es constante

Utiliza árboles rojo y negro

Uno de los focos de eficiencia del CFS está en la implementación de las políticas anteriores. Pero también en una buena selección del tipo de dato cuando el planificador debe encontrar el próximo job a ser ejecutado

  • Las listas no escalan bien
  • Los árboles sí, en este caso los árboles Rojo-Negro

Para tener una idea cerrada de donde aparece el árbol rojo y negro dentro del kernel de Linux a partir de la versión del kernel 2.6.0

El algoritmo

Cuando el scheduler es invocado para correr un nuevo proceso, la forma en que el scheduler actúa es la siguiente:

  1. El nodo más a la izquierda del árbol de planificación es elegido (ya que tiene el tiempo de ejecución más bajo), y es enviado a ejecutarse
  2. Si el proceso simplemente completa su ejecución, este es eliminado del sistema y del árbol de planificación
  3. Si el proceso alcanza su máximo tiempo de ejecución o de otra forma se para la ejecución del mismo voluntariamente o vía una interrupción, este es reinsertado en el árbol de planificación basado en su nuevo tiempo de ejecución (vruntime)
  4. El nuevo nodo que se encuentra más a la izquierda del árbol será ahora el seleccionado, repitiéndose así la iteración