Está en la página 1de 10

Planificador del kernel Linux.

Gabriel Núñez Núñez-Lagos - Juan Antonio Rodríguez Vela

1 Introducción.
Linux es un núcleo (kernel) de sistema operativo. Habitualmente se distribuye junto con una serie de aplicaciones desarrolladas por el proyecto GNU para formar un sistema operativo completo llamado GNU/Linux. Es software libre, lo que quiere decir, fundamentalmente, que se puede leer o modificar el código, distribuir (original o modificado), copiar y utilizar libremente. No se permite plagiarlo, ni distribuirlo sin dar acceso al código fuente, ni cambiarle la licencia. Linux debe su nombre a su desarrollador original, Linus Torvalds, que era estudiante de Informática en una universidad de Helsinki (Finlandia) cuando en 1991 se decidió a hacer un núcleo de sistema operativo que funcionara como MINIX (un derivado de UNIX). Es muy compatible con UNIX, pero no es un UNIX en el sentido de que por dentro funciona como han querido Linus y los cientos de desarrolladores que trabajan en él por Internet desde su creación. Desde hace unos meses se encuentra en su versión 2.6 (la 2.6.5 era la última el 20 de Abril de 2004). La versión de producción anterior, la 2.4 (2.4.26 era la última el 23 de Abril de 2004), se sigue utilizando mucho. Las versiones con segunda cifra por la izquierda impar son de desarrollo. El planificador (scheduler) es la parte del sistema operativo que permite la multiprogramación y el multiproceso mediante la asignación de procesos a las CPUs del sistema y el intercambio de procesos en ejecución. Ha cambiado completamente de una versión a otra, se mantienen las principales estructuras de datos, pero se ha cambiado el algoritmo para hacerlo O(1) y con ello, más escalable1. La versión más documentada es la 2.4, así que hablaremos fundamentalmente de ella.

2 Características generales.
y

Linux es un kernel monolítico, como la mayoría de *NIX. Eso quiere decir que el núcleo hace todo (proporciona todos los servicios del sistema operativo) y todas las capas del núcleo tienen acceso a todas las estructuras de datos, rutinas y componentes del sistema.

No está diseñado a partir de hilos de núcleo. 3 Gestión de procesos en linux...y y y y y y y Es capaz de linkar módulos en tiempo de ejecución. el PCB es la estructura struct task_struct. Al compilar el núcleo se da con cada módulo la opción de incorporarlo estáticamente al binario del kernel o bien compilarlo aparte para que luego se pueda cargar según demanda.En Linux. No expropiativo (Non-preemptiveness).6: en este último el kernel puede interrumpirse a sí mismo para atender a ciertas interrupciones. Quiere decir que un proceso en modo núcleo no puede ser arbitrariamente interrumpido. en principio. El kernel no está implementado como un conjunto de hilos. sistemas de archivos para todos los gustos. . en una CPU que esté libre sin estorbar a los procesos en modo núcleo que se estén ejecutando en otras CPUs. Admite multiproceso simétrico (SMP). Era una característica del núcleo 2. Antes del kernel 2. bloque de control del proceso o descriptor del proceso. Tiene un diseño reentrante de modo que pueden existir varios procesos en modo núcleo 'ejecutándose' a la vez (en monoprocesadores. los módulos tienen la ventaja de ser actualizables. Además de la reducción en el uso de memoria. Compatible con estándares: POSIX. Los módulos contienen funcionalidades particulares que son cargadas dentro del Kernel cada vez que se requiere y no están permanentemente alojados dentro del Kernel en sí. Aunque realmente los admite: utiliza unos pocos para realizar tareas repetitivas2 y no tienen permitido ejecutar programas de usuario. porque si tenemos más de una CPU.0 los hilos en aplicaciones de ususario se gestionaban desde librerías en modo usuario. un nuevo proceso en modo núcleo podrá ejecutarse. ni dentro de la memoria. En esta estructura aparece todo tipo de información sobre un proceso. todos menos uno estarían en la cola de listos). En todo sistema operativo un proceso está representado por una estructura de datos donde se guarda toda la información relevante de éste. Dicha estructura forma parte de una estructura del tipo union task_union que contiene el PCB (struct task_struct) y la pila del núcleo de dicho proceso (para cuando el proceso se ejecuta en modo núcleo). en la que la pila y el PCB comparten la memoria ocupa . el PCB (Process Control Block).Cada proceso del sistema tiene una estructura del tipo struct task_struct asociada. las APIs del UNIX SysV y los sockets BSD. Esta estructura. Realmente sólo tiene interés3 en monoprocesadores. Tiene soporte para aplicaciones de usuario multihilo.4 que ha cambiado en el 2. al contrario que otros *NIX modernos.

y y La tabla hash es usada para encontrar rápidamente una tarea por su pid usando find_task_pid(). Esto es realizado bajo la protección de un spinlock 4 read/write ya que un acceso no controlado a la tabla hash podría traer consecuencias desatrosas. static inline struct task_struct *find_task_by_pid(int pid) struct task_struct *p. se usa encadenamiento. Las tareas en cada lista ordenada (esto es. ordenados por el pid y como una lista circular doblemente enlazada usando los punteros p-next_task y pprev_task. return p. En caso de colisión en la tabla hash.Nótese que el uso de semáforos no sería suficiente para proporcionar protección en caso de múltiples cpu´s.8KB. **htable = pidhash[pid_hashfn(pid)]. 3. p p-pid != pid. El conjunto de procesos en el sistema Linux está representado como una colección de estructuras struct task_struct.Con esta estructura.1 Análisis del descriptor de proceso (struct task_struct) El principal tipo de dato que se utiliza para gestionar los procesos en el núcleo es task_struct. p = p-pidhash_next). unsignedlong stack[INIT_TASK_SIZE/sizeof(long)]. el descriptor de proceso. ordenadas por el mismo valor dado por la función hash) son enlazadas por p->pidhash_next/pidhash_pprev el cual es usado por hash_pid() y unhash_pid() para insertar y quitar un proceso dado en la tabla hash. union task_union struct task_struct task. La lista circular doblemente enlazada que usa p-next_task/ prev_task es mantenida para poder ir fácilmente a través de todas las tareas del sistema. las cuales están enlazadas de dos formas principalmente: Como una tabla hash. Una instancia de este tipo de dato puede . Son las llamadas colisiones */ for(p = *htable. /* Se recorren los elementos cuyo valor dado por la función hash es el mismo. el núcleo es capaz de determinar el puntero al PCB de un proceso a partir de su puntero de la pila de núcleo (se hace con la macro current que ejecuta tres instrucciones de ensamblador).

no es más que una subestructura incrustada dentro del descriptor del proceso. debe abandonar la CPU y por lo tanto se deberá invocar al planificador en el momento adecuado (en vez de llamar a schedule() directamente). Muchas partes del S. 0 parado */ #define TASK_RUNNING 0 /* Listo. El valor típico es de 3GB para procesos de usuario y 4GB para los hilos de ejecución del núcleo. SIGTTIN. por ejemplo. 0 ejecutable./*-1 no ejecutable. así es: un puntero al anterior y al siguiente están incrustados dentro de nuestra task_struct. ¿EL truco? Lo que solemos usar como nodo en una lista enlazada (los punteros anterior y siguiente). ejecución */ #define TASK_INTERRUPTIBLE 1 /* Dormido (bloqueado). Algunos de estos campos son: y volatile long state: contiene el estado del proceso. mm_segment_taddr_limit: indica el tamaño máximo del espacio de direcciones de dicho proceso. SIGTSMP.ocupar normalmente hasta cerca de 1000 bytes. int sigpending: Otros campos que contienen información general del proceso son: . o de un bloqueo o suspensión. */ #define TASK_ZOMBIE 4 /* Esperando un wait() */ #define TASK_STOPPED 8 /* SIGSTOP. Admite señales */ #define TASK_UNINTERRUPTIBLE 2 /* Bloqueado pero sólo responde a la señal o interrupción que espera. por lo que es necesario conocer los campos más importantes de task_struct. Depuración*/ #define TASK_EXCLUSIVE 32 y y y un valor distinto de cero (típicamente 1) indica si el proceso tiene señales pendientes. 0 ejecutable.O. /* -1 no ejecutable. posiblemente. 0 parado*/ volatile long state. Que la variable esté declarada como volatile le indica al compilador que su valor puede cambiarse de forma asíncrona (Por ejemplo desde una rutina de tratamiento de interrupción). volatile long state. Está muy bien aprovechada: todas las estructuras y tablas que trabajan con procesos están implementadas de tal forma que una misma de estas task_struct puede formar parte de muchas tablas o listas a la vez. hacen uso de esta estructura de datos. volatile long need_resched: con su valor a 1 indica que este proceso. Se reacciona a estas señales al volver de una llamada al sistema. SIGTTOU.

tu hermano inmediatamente más joven).c(Youngest Child). unsigned long blocked: Contiene un mapa de bits con las señales que están temporalmente bloqueadas. El algoritmo de planificación es bastante parecido a un Round Robin con prioridades. hermano directamente más viejo). contendrá el número de señal que lo mató.y int exit_code. *p_cptr. En realidad cada proceso se puede planificar de . sgid. Aunque la mayor parte del estado de un proceso se ha salvado en la pila del núcleo con la macro SAVE_ALL. long start_time: Indica el instante de creación de este proceso. El campo que contiene esta información es struct Algunos de sus campos son: y y y unsigned long esp0. egid.1b. 4 Descripción del algoritmo de planificación de procesos. Usuario propietario de este proceso. thread_struct thread. y atributos más específicos.4). fsgid: Grupo propietario de este proceso. como efectivo (euid). gid_tgid. *p_ysptr. Registros de depuración. *p_osptr: y y y y y y Punteros para acceder a toda la familia de procesos: op(Original Parent). existen algunos registros adicionales que se almacenan en la estructura task_struct y que son utilizados sobre todo en el cambio de contexto. unsigned long debugreg [ 8 ] . Los tipos de algoritmos de planificación usados en este núcleo están definidos en las extensiones de tiempo real del estándar POSIX (POSIX. Puede haber a la vez en el sistema procesos con distinta política de planificación establecida. struct task_struct*p_opptr. antes llamado POSIX. y atributos más específicos. os (Older Sibling.p(Parent). *p_pptr. uid_tuid. Puntero de instrucción y de pila del proceso y otros registros de segmento del i386. como efectivo (egid). euid. unsigned long eip. exit_signal: contiene el valor de terminación de un proceso. tanto real (uid). fs. tanto real (gid). gs. fsuid. Puntero de pila del núcleo.ys (Younger Sibling. struct sigpending pending: Contiene la información sobre las señales que este proceso tiene pendientes. esp. suid. Si termina por una señal. en caso de que haya finalizado mediante la llamada al sistema exit(2).

El siguiente proceso se escoge de la lista de procesos con SCHED_RR o de la lista SCHED_FIFO.varias maneras. ejecutar ya) de ejecutar un proceso evaluando la prioridad. aunque normalmente no se use porque con esta planificación no se fuerza al proceso a abandonar la CPU.1 El algoritmo del kernel 2. Se la puede llamar explícitamente o la puede llamar el sistema operativo: a la vuelta de una llamada . nada deseable y +1000. La prioridad estática se guarda en un campopriority del descriptor de proceso y se usa para inicializar el valor de nice además de marcar el valor inicial del cuanto de tiempo. Por una parte se usa una función goodness que mide lo deseable (entre -1000. Se activa con una llamada al sistema (sched_yield()) sólo para el siguiente ciclo de planificación. tiempo real. La función schedule realiza el ciclo de planificación.4. No es aplicable a tiempo real. El proceso cede la CPU a cualquier otro que esté listo. se favorece a los procesos que se hayan estado ejecutando en la misma CPU (si se usa SMP) y se añade el valor de nice que especificó el usuario. Por otra parte. las políticas de planificación de un proceso se pueden cambiar en tiempo de ejecución y son: y y y y : Es la planificación clásica de UNIX. Examina las prioridades dinámicas (calculadas como combinación de las especificadas por el usuario y las calculadas por el sistema). existe una función sys_nice o sys_setpriority que se usa para aumentar la prioridad estática del proceso. Si no. SCHED_FIFO : El sistema FIFO o FCFS (First to Come is First Served). Los procesos esperan en cola y se ejecutan secuencialmente. Si el proceso no es de tiempo real. se aumenta ligeramente la prioridad de los hilos del mismo proceso. Se usa en procesos de tiempo real. Funciona como el FIFO pero ahora cuando un proceso acaba su cuanto de tiempo (time slice) se le desaloja. La prioridad de un proceso se calcula sumando. sino un modificador que afecta a las tres políticas anteriores. Los procesos que llevan más tiempo en el sistema van perdiendo prioridad. Se sigue calculando un cuanto de tiempo para el proceso. SCHED_RR : Round Robin o turno rotatorio. los procesos en tiempo real tienen un campo extra de prioridad que se suma a 1000 para el cálculo de goodness. Son procesos en tiempo real. se parte del tiempo restante de CPU (prioridad dinámica guardada en counter) de forma que tienen más prioridad aquellos a los que les quede más tiempo de CPU. SCHED_YIELD : No es una política de planificación. SCHED_OTHER 4.

en caso contrario se eleimina de la cola. independientemente de las circunstancias que rodean la ejecución. luego si la política del proceso ya ejecutado es RR se comprueba su cuanto de tiempo y si ha acabado. Se divide por dos el tiempo de CPU (valor de counter que marca el tiempo restante) y se le suma la prioridad estática para obtener el nuevo valor del contador.al sistema. que apuntarán al proceso que estaba en ejecución y al proceso que se va a pasar a ejecutar. Tal como indicaba Ingo Molnar cuando anunció el nuevo planificador en enero de 2002 (kernel 2.2-pre6) se pretendía mantener las cosas buenas del planificador antiguo y añadir 'algunas pocas': y y O(1): Se eliminan unos bucles que recalculaban datos para las rutinas wakeup(). Estos cálculos se hacen con la función recalculate que en este kernel resultaba poco eficiente.2 Diferencias en el kernel 2. schedule() y la interrupción por tiempo. Escalabilidad con multiproceso simétrico. Podría no ser 0 si se hubiera llamado a goodness() con el bit SCHED_YIELD activo. Ahora se hacen con algoritmos que tardan un tiempo constante.6. 4. Este hilo se encarga de planificar los procesos de la cola de ejecución de la CPU en la que se ejecuta. Si no encuentra ningún proceso con cuanto por ejecutar se restablece el cuanto de cada de las tareas. Si el proceso actual no está en TASK_RUNNING se quita de la cola de preparados.y si está en estado TASK_INTERRUPTIBLE se le enia una señal si hay que despertarlo.5. Además. por llamada del sistema o cuando se ha acabado la cola de ejecución para equilibrar la carga con otras CPUs. Lo normal es que el valor del contador anterior sea 0 por lo tanto estariamos inicializando el contador a la prioridad estática. después de poner el proceso actual en la cola de espera o por temporizador. El nuevo planificador ejecuta un hilo de núcleo en cada CPU. Más tarde se elimina la marca que indica la necesidad de planificación.el proceso es enviado al final de la cola de listos. En primer lugar se definen los punteros a la tabla de procesos prev y next. y que se va recorriendo en busca del mayor valor devuelto por goodness. Antes se usaba . pudiendo cambiar dinámicamente la política de planificación y la prioridad del proceso. se lleva la cuenta del comportamiento del proceso. si se dedica a entrada/salida o tiene carga de CPU. Existe una única cola de procesos que empieza en INIT. Dos procesos en dos CPUs distintas se ejecutan de forma totalemente independiente. Se llama a este hilo por temporizador.

Linux usa la misma estructura de datos (task_struct) para representar un proceso y para representar un hilo. Ejecutar los hijos antes que el proceso padre. Desde los primeros días del soporte Linux los desarrolladores se encararon con el clásico problema de acceder a datos compartidos entre los diferentes tipos de contexto (procesos de usuario vs interrupciones) y diferentes instancias del mismo contexto para múltiples cpus. Un proceso tiende a ejecutarse en el mismo procesador después de un bloqueo o suspensión. de forma que todos los hilos de un mismo proceso comparten exactamente el mismo espacio de direcciones.-) . Los procesos por lotes utilizan la CPU cuando no hay otros procesos listos: tienen menos prioridad que cualquier otro proceso pero sus cuantos de tiempo son mucho mayores. Capacidad para manejar grandes cargas sin colapsar la planificación. Los intervalos de tiempo se distribuyen en cada CPU por separado. Mayor afinidad por el procesador. Se ha añadido SCHED_BATCH a los posibles valores de la política de planificación. al crear un hilo lo que se hace es copiar los punteros.y y y y y una sola cola para todos y un semáforo runqueue_lock para todos los procesos de todas las CPUs. La sincronización y exclusión mutua del acceso concurrente de los hilos a la memoria del proceso es responsabilidad del programador que los usa . el algoritmo también es O(1). que usaba cola FIFO. Para la planificación en tiempo real (RT) . Esto supone una ventaja importante para la planificación: se planifica cada hilo como si fuera un proceso. . La peculiaridad es que esta estructura tiene unos campos que son punteros al espacio de direcciones de un proceso. 5 Apéndice: Hilos y procesos.Light Weight Process). 6 Apéndice2: Spinlocks. Planificación por lotes. Mejora la eficiencia. No se le cambia de procesador sin motivo. A veces a esto se le llama proceso ligero (LWP. ¿En qué se diferencia un proceso hijo de un hilo? Al crear un proceso hijo lo que se hace es copiar la memoria del padre en otra dirección y hacer que estos punteros señalen a la nueva.

y mientras cli() suministra protección contra las carreras con el contexto de interrupciones en cada CPU individualmente. /* sección crítica */ spin_unlock(lock). 7 Bibliografía y y y y y y y y y y y y http://ftp.5. cli().hpl.txt http://es.com/research/linux/kernel/o1.ulpgc.org/pub/linux/kernel/people/davej/misc/post-halloween2.fi/O%281%29-scheduler.6/linux-2. no suministra protección contra todas las carreras entre los contextos funcionando en diferentes CPUs.pdf http://www.html http://nulies. Sebastopol.6.6.iki.kernel. Mientras que esto está bien en UP. /* código crítico */ restore_flags(flags). Bovet.html http://www.org Understanding The Linux Kernel.4.html#toc2 http://www.org/Manuales-LuCAS/DENTRO-NUCLEO-LINUX/dentronucleo-linux-html/dentro-nucleo-linux.arstechnica.com/etc/linux/index.php http://www. entonces la forma de protegerlo usando las instrucciones cli/sti en UP es: save_flags(flags).4/linux-2.org/pub/linux/kernel/v2. Marco Cesati.26. http://sopa.tar.tldp. Daniel P.org/pub/linux/kernel/v2. Clifornia Ed. Ejemplo de uso: spin_lock(lock). Ocurriría lo mismo si usasemos semáforos Es aquí donde los spinlocks son útiles.es/iidso/leclinux/procesos/planificador/LEC6_CHEDULER.Si la región crítica del código puede ser ejecutada por el contexto de los procesos y el contexto de las interrupciones.txt http://www. obviamente no lo está usándolo en SMP porque la misma secuencia de código quizás sea ejecutada simultáneamente en otra cpu. 2ª Edición.dis.es/ http://safari.tar.kernelnewbies.bz2 http://www.bz2 8 Condiciones de copia y distribución.kernel.escomposlinux. . O'Really 2003.hispalinux.org/wwol26/wwol26.hp.kernel.

.Copyright 2004 Juan Antonio Rodríguez Vela. kflush (bdflush)...nnl@estudiante. .gnu. proceso1. .es). Se puede copiar..6 hay además un hilo en cada CPU que se encarga de balancear la carga entre procesadores.txt Se considerará como código fuente el código HTML. Gabriel Núñez Núñez-Lagos (gabriel. En el 2.spinlock 4 Los spinlocks son una de las formas de controlar la concurrencia en el kernel.adi. kswapd. interés3 El interés recaía en que se facilitaba el diseño del kernel si se consideraba que las estructuras de datos no podían ser modificadas por otros procesos durante la ejecución de un proceso en modo núcleo.uam. distribuir usar y modificar según los términos de la Licencia Pública General de GNU (GPL.. kapm.GNU General Public License).es Notas al pie .. .org/licenses/gpl. Hablaremos de ellos en el apéndice segundo.. ksoftirqd. . escalable1 La habilidad de manejar con eficiencia distintas cantidades de recursos y CPUs. Tiene una copia de esta licencia en http://www. Copyright (C) 2004 Juan Antonio Rodriguez & Gabriel Nunez charla-kernelv1-0-html.uam..html :: descargado de la sección de documentación de la Asociación para el Fomento del Software Libre: http://afsl. kupdated.. keventd. Sobre este documento. Este documento es software libre.. repetitivas2 Esos hilos son: proceso0. desde pequeños procesadores empotrados hasta sistemas multiprocesador se conoce como escalabilidad.