Subprocesos (hilos) Introducción: Este apunte tiene como objetivo facilitar al alumno la comprensión del concepto de hilos, su funcionamiento, administración, ventajas y desventajas. El termino hilos, como traducción literal de thread, ha dado lugar a múltiples confusiones y dificultades para su comprensión, por lo que lo reemplazare en este texto por subproceso, por entender que es mas explícito, pero mantendré multihilo como sinonimo de mulithread, ya que multisubprocesos no es para nada claro. Conceptos básicos: Proceso tradicional (o pesado) Un proceso es la unidad de trabajo de un sistema de cómputos. Está constituido por una sección de texto (código del programa), una sección de datos, y su PCB (Procces Control Block). La idea intuitiva es que un proceso es un programa en ejecución, o sea, el código mas todos los recursos necesarios para su ejecución. Todo proceso tiene una serie de elementos: • Identificador • Estado • Prioridad • Contador(es) de programa • Punteros a memoria • Valor de los registros de CPU • Información de E/S • Información de seguridad • Información estadística • etc.. Los datos sobre el proceso el Sistema operativo los guarda en el bloque (o registro) de control de procesos, PCB, y la administración de procesos utiliza la lista de PCBs para manejarlos. Gráficamente podemos imaginar una PCB de esta manera: PCB Pila del proceso Registros de la CPU Puntero próxima Inst. Espacio de direcciones Datos estadísticos Datos de auditoria Datos de periféricos ................................ ................................ Cambio de Proceso (o Contexto) Los sistemas operativos multitarea administran el uso de la CPU a través del planificador de corto plazo, que selecciona cuál de los procesos cargados en memoria tomará la CPU cuando ésta queda libre. Cuando un proceso realiza una llamada al sistema operativo, este toma el control de la CPU, realiza las tareas necesarias y carga el nuevo proceso para su ejecución. esta tarea de cambio de proceso, en alguna bibliografía se denomina cambio de contexto. Otros momentos en los que puede realizarse un cambio de proceso es cuando el sistema operativo toma el control de la CPU por una interrupción de reloj, de E/S, o por un manejo de error (excepción) reversible, como fallo de página. Para poder retomar la ejecución del proceso que deja la CPU, debe salvaguardarse todos los datos necesarios para retomarlo, sin duda, los datos de la pila, el contador del programa, los registros del procesador, y todo otro dato necesario. Estos datos se guardan en la PCB. Llevar a cabo esto requiere tiempo, medidos en ciclos de CPU. Problemáticas con el procesamiento secuencial con rutinas: Esta forma de trabajo se utilizó hasta mediados de la década del 90, pero trajo implicado una serie de problemas. Veamos como ejemplo el uso de un procesador de palabra típico. Con la interfaz texto, la rutina básica era: Hacer tomar tecla analizar tecla mostrar tecla en pantalla ; estos editores trabajan en modo comando y en modo inserción, de modo que lo observado en pantalla no es lo que vamos a imprimir, por ej. el VI de UNIX Luego se desarrollaron los editores WYSIWYG, “lo que ves es lo que tenés”, como el Emacs; esto implicó que la rutina de analizar tecla fuera cada vez mas compleja. A posteriori, el desarrollo de la interfaz gráfica trajo aparejado mas requerimientos, y se fueron incorporando mas funcionalidades, como la corrección ortográfica en línea, la auto corrección, el reemplazo automático de textos, etc. Hoy en día, el código sería Hacer tomar tecla analizar tecla Si tecla es blanco entonces Llamar rutina de Ortografía Llamar rutina de Gramática Llamar rutina de Reemplazo automático llamar rutina de ... sino mostrar tecla en pantalla ; con lo que tendríamos que prepararnos un mate y cebar cada vez que pulsamos blanco, por la lentitud de los tiempos de respuesta. El sistema tiene que ejecutar las rutinas. O sea, la forma tradicional de ejecutar procesos no permite ciertas funcionalidades, y no optimiza el uso de la CPU, ya que si estas rutinas realizan una llamada al sistema operativo, tal como consultar archivos, etc., todo el proceso queda suspendido. Respuesta con subprocesos: Ahora bien, no necesitamos que la corrección ortográfica o el reemplazo automático sea inmediato, podemos seguir escribiendo y dejar que esta función se realice en background, “detrás” de la rutina principal. Si el subrayado en rojo aparece luego que escribamos dos o tres letras de la próxima palabra, no modifica la funcionalidad. Si esto lo implementamos mediante procesos tradicionales, donde al iniciar la ejecución de un procesador de palabras también iniciáramos un conjunto de procesos, corrector ortográfico, corrector semántico, etc., los tiempos de cambio de proceso disminuirían drásticamente la velocidad de respuesta . Pero esto no es necesario, los distintos procesos comparten gran cantidad de datos, el usuario, la posición en pantalla, etc. por lo que cambiando solamente los datos específicos podemos implementarlo. Esta es la idea básica de un sistema multihilo, cada proceso ahora, en ves de tener un solo hilo de ejecución, tiene varios, o sea, generamos varios subprocesos dentro de cada proceso. Para administrar esto, creamos el registro TCB, Thread Control Block, que va a tener solamente los datos que cambian entre subproceso y subproceso, gráficamente podemos verlo así: PCB Espacio de direcciones Datos estadísticos Datos de auditoria Datos de periféricos ................................ TCB 1 Pila del subproceso Registros de la CPU Puntero próxima Instrucción del subproceso. TCB 2 Pila del subproceso Registros de la CPU Puntero próxima Instrucción del subproceso. TCB n Pila del subproceso Registros de la CPU Puntero próxima Instrucción del subproceso. Entonces tenemos un proceso que tiene n subprocesos, con n variando desde 1 en adelante. De esta manera, el tiempo de cambio de un subproceso a otro es mucho menor que el tiempo de cambio de un proceso a otro, ya que numerosos datos son compartidos. En nuestro ejemplo del procesador de palabra, cuando el subproceso tomar tecla está en espera, puede tomar la CPU el subproceso control de ortografía, de esta manera, mantenemos un buen tiempo de respuesta y brindamos todas las funcionalidades deseadas. El uso de subprocesos mejora el rendimiento de los sistemas con múltiCPU, (antes llamados sistemas multiprocesadores, pero desde la aparición de procesadores multicore, con dos o mas CPUs integradas, tuvimos que cambiar el nombre), ya que distintos subprocesos pueden ejecutarse simultáneamente en distintas CPU, teniendo un procesamiento en paralelo dentro del mismo proceso principal. Ventajas Inclusive en sistemas monoCPU la el uso de subprocesos brinda múltiples beneficios, por ej: (Tomados de Letwin) Subproceso en primer plano y múltiples subprocesos en segundo plano: El ejemplo dado del Procesador de palabras o una planilla de cálculo, en la que mientras el usuario ingresa un dato en segundo plano se calculan los valores de otras celdas Procesamiento asíncrono: Crear una copia de seguridad del texto cada minuto puede ser un subproceso. Incremento de la velocidad de ejecución: Si un proceso debe leer datos y sumarlos, podemos dividirlo en subprocesos, mientras uno lee, y por lo tanto libera la CPU mientras se atiende su llamada al sistema, el otro suma. Estructura modular de programas: Claramente al dividir en subprocesos creamos una estructura modular. Estas son solo algunas, sería imposible listar todos los usos ventajosos posibles. Desventajas También pueden generarse múltiples dificultades por el uso de subprocesos, dentro de ellas cabe destacar el incremento de la necesidad de sincronización. Sistemas que generaban automáticamente exclusión mutua entre rutinas, porque al ejecutarse una no se ejecutaba la otra, al migrarlo a sistemas con subprocesos debemos sincronizar su funcionamiento incorporando semáforos, monitores y otras herramientas. Características de los subprocesos Todos los subprocesos de un proceso principal: • comparten el mismo espacio de dirección. • comparten los recursos de E/S asignados, por ej. Archivos. • pueden ser afectados por cualquier otro subproceso del mismo proceso principal. Son, por lo tanto, interdependientes. Ciclo de vida de los subprocesos (estados) Los subprocesos en general pueden tener los siguientes estados: • Nuevo • Listo (Ready): En capacidad de ejecutarse • Ejecución (Run): ejecutándose • Bloqueado (Lock) • Finalizado Los Threads en Java: • • • • Nuevo (New) Ejecutable (Runnable): Equivalente a los estados Listo y Ejecución generales Bloqueado (Blocked) Finalizado (Dead) Subprocesos a nivel Usuario y a nivel Núcleo Existen múltiples implementaciones de subprocesos en las distintas plataformas, veamos en primera instancia las implementaciones a nivel usuario y a nivel núcleo. Nivel Usuario: Denominado ULT (user-level threads), su implementación está a cargo totalmente de la aplicación, a través de una biblioteca de hilos. El sistema operativo administra el proceso como una unidad, y la aplicación migra de un subproceso a otro con su propia administración. Si bien existen bibliotecas que permiten incorporar subprocesos en lenguajes, como las GNU Portable Threads ( http://www.gnu.org/software/pth/ ), su uso en compiladores no diseñados a tal efecto no es recomendable (http://pag.csail.mit.edu/readinggroup/boehm05threads.pdf ). Aclarando el concepto, así como un programador maneja el cambio de una rutina a otra, y esto es transparente al sistema operativo, así puede manejar el cambio de un subproceso a otro, si tiene implementada esta opción en su lenguaje. Las ventajas son evidentes, cuando un proceso perdería uso de la CPU por esperar un evento, aun cuando su quantum de tiempo no esté agotado, bajo este modelo, simplemente migra a otro subproceso. Toda aplicación inicia con un solo hilo de ejecución, y durante su ejecución, va creando y finalizando distintos hilos de acuerdo a sus necesidades. Toda esta actividad se desarrolla en el espacio de usuario y dentro de un solo proceso, el que ve el sistema operativo. Dentro de las ventajas de uso de los ULT sobre los KLT podemos mencionar: • Se eliminan las perdidas de tiempo por Cambio de contexto ya que todo sucede dentro del mismo proceso. • El tipo de planificación, como prioridad o FCFS , puede variar de aplicación a aplicación, de acuerdo a las necesidades de cada una. • Pueden ser ejecutados en cualquier sistema operativo. En las desventajas, podemos nombrar : • Cuando un ULT realiza una llamada al sistema operativo, detiene todos los ULT, ya que el proceso pasa a espera, o Hold, o sleep. • No puede hacer uso del paralelismo en caso de existir mas de una CPU. Gráficamente, podemos verlo así: Hilo inicial Espacio de Usuario Biblioteca de Hilos Espacio de Núcleo Proceso Nivel Núcleo: Denominados KLT (Kernel-level threads), pueden ser implementados en forma pura o combinada. en forma pura la relación entre ULT y KLT es uno a uno, y en forma combinada es varios a varios, siendo la cantidad de KLT menor o igual que la de ULT. Forma pura En una implementación pura, como la de Windows XP, la administración de los subprocesos está totalmente a cargo del sistema operativo, que provee una interfaz de programación (API) para utilizarla. Gráficamente, podemos verlo así: Espacio de Usuario Espacio de Núcleo Proceso Todas las aplicaciones son formalmente hablando, multihilo, a lo sumo, tienen un solo hilo. Todos los hilos (subprocesos) pertenecen al mismo proceso. El sistema operativo mantiene la información del proceso general y de cada subproceso, como se explicó en PCB y TCB. La planificación del uso de la CPU es a nivel subproceso. Esto es importante de entender, ya que resuelve las desventajas planteadas en el modelo de ULT, pero incorpora una perdida mínima de tiempo de cambio de contexto durante el cambio de un subproceso a otro dentro del mismo proceso. Si el uso de las funciones del sistema operativo a través de las llamadas es extensivo, tal como sucede en los procesos con interfaz gráfica, el uso de ULT exclusivamente no es superior al uso de KLT puro, ya que igualmente es necesario el cambio de contexto. Forma combinada En los enfoques combinados ULT/KLT, como el usado en Solaris 9, el usuario crea ULT, y la mayor parte de su administración se realiza en el ambiente de usuario. Para el sistema operativo la administración es a nivel subprocesos, con lo que se mantiene el alto nivel de concurrencia y la posibilidad de ejecución en paralelo en sistemas multiCPU . De esta manera se obtienen las ventajas de los modelos anteriores. Gráficamente, podemos verlo así: Biblioteca de Hilos Espacio de Usuario Espacio de Núcleo Hilos a nivel Núcleo Procesos