Podemos diferenciar dos procedimientos en el kernel:
- La emigración de hilos: cuando los hilos se dirigen hacia otro procesador diferente a donde se está ejecutando el hilo que los creo.
- La inmigración de hilos: cuando el procesador huésped encola en el planificador local a los hilos que provienen de otros procesadores.
Este es el único punto del núcleo en el cual se requiere de algún tipo de mecanismo de sincronización entre los procesadores para poder enviar y recibir los procesos que deben migrar. El mecanismo es denominado “Exchange Slot”, y permite la comunicación entre procesadores sin utilizar operaciones atómicas. En este caso particular, se utiliza para el envío de nuevos hilos, pero puede ser utilizada para el envío de cualquier tipo de datos.
Por cada procesador existe una estructura denominada TSchedulerExchangeSlot:
TSchedulerExchangeSlot = record
DispatcherArray: array[0..MAX_CPU-1] of PThread;
EmigrateArray: array[0..MAX_CPU-1] of PThread;
end;
Siendo MAX_CPU el número de procesadores y PThread un puntero a una estructura TThread. De la definición de la estructura, se observa que cada procesador posee dos arreglos (DispatcherArray y EmigrateArray), y cada uno puntea a un grupo de hilos.DispatcherArray: array[0..MAX_CPU-1] of PThread;
EmigrateArray: array[0..MAX_CPU-1] of PThread;
end;
El procedimiento de envío de hilos hacia otros procesadores puede ser descripto en tres niveles:
1 – El usuario invoca al procedimiento BeginThread()para crear un nuevo hilo, si el parámetro CPUID es diferente al identificador de la CPU local, el kernel sabe que este nuevo hilo deberá ser migrado, y entonces lo agrega a la lista ligada DispatcherArray[CPUID].
2 – Cuando se ejecuta el planificador del procesador local (se invoca la llamada al sistema SysThreadSwitch), el procedimiento Emigrating() chequea si alguna de las entradas del arreglo EmigrateArray[] es nula. De ser así, desplaza todos los hilos desde la entrada correspondiente a DispatcherArray[] hasta EmigrateArray[].
3 – Durante la planificación de un procesador remoto el procedimiento Inmigrating() chequea la entrada del arreglo EmigrateArray[] correspondiente a su CPUID de la estructura TSchedulerExchangeSlot de cada procesador. Si no es nula, esta contiene un puntero al primer elemento de una cola ligada de procesos que están intentando migrar hacia su procesador. El procedimiento toma la cola de hilos y los coloca en la lista de hilos listos para ser ejecutados, luego vuelve a la entrada en el arreglo EmigrateArray[] nula.
Es claro que el arreglo DispatcherArray[] es de lectura/escritura solo para el procesador local. Mientras el arreglo EmigrateArray[] es de escritura/lectura para el procesador local y remoto, pero que la escritura sobre éste es sincronizada a través de la utilización del puntero nulo.
El “Exchange Slot” no requiere de ningún procedimiento que involucre un “pooling” de una variable del tipo “Lock”, evitando la utilización de operaciones atómicas y todo lo que ellas conllevan.
La inmigración y emigración de hilos entre procesadores se realiza únicamente cuando se invoca al planificador -es decir- cuando el hilo cede el procesador al kernel. En ese momento el Sistema realiza tareas de mantenimiento. Como se observa en la figura, cuando se llama al planificador, éste primero realiza la inmigración de hilos, luego realiza la emigración de hilos, y finalmente realiza la planificación de un nuevo hilo para el procesador local.
Matias E. Vara
www.torokernel.org