Clase 4: Procesos, Threads (Hilos)

Anuncio
Clase 04: Procesos
PROCESOS Y THREADS
El concepto de proceso se origina en el campo de los sistemas operativos, donde comúnmente se define
como un programa en ejecución.
Desde la perspectiva de un sistema operativo, la administración y programación temporal de los
procesos es tal vez el aspecto más crítico. Sin embargo, en los sistemas distribuidos hay otras cuestiones
que son igualmente importantes.
Por ejemplo, para organizar eficientemente sistemas cliente-servidor, frecuentemente resulta
conveniente utilizar técnicas multithreading (multihilo). La principal contribución de los threads (hilos)
en los sistemas operativos es que permiten que clientes y servidores sean construidos de tal manera que
la comunicación y el procesamiento local puedan traslaparse en el tiempo, permitiendo con ello
mayores niveles de desempeño.
Aunque los procesos constituyen el bloque básico en la implementación de sistemas distribuidos, la
práctica muestra que el plantear la granularidad (división) de un sistema distribuido en diferentes
procesos, tal como lo establecen los sistemas operativos en los que se construyen los sistemas
distribuidos, no es suficiente. Resulta que plantear una granularidad más fina en la forma de threads
múltiples de control por proceso, hace más fácil desarrollar aplicaciones distribuidas y obtener un mejor
desempeño.
4.1 Introducción a Procesos y Threads
Para entender el papel que juegan los threads en los sistemas distribuidos, es importante entender qué
es un proceso, y como se relacionan los procesos y los threads. Al ejecutar programas, el sistema
operativo crea un cierto número de procesos virtuales, cada uno para correr un programa diferente. Con
la finalidad de mantener un control y seguimiento de todos estos procesadores virtuales, el sistema
operativo usa una tabla de procesos, la cual contiene entradas para almacenar los valores de los
registros del CPU, mapas de memoria, archivos abiertos, información del proceso, privilegios, etc. Un
proceso es frecuentemente definido como un programa en ejecución, es decir, un programa que está
siendo ejecutado en uno de los procesadores virtuales del sistema operativo. Un asunto importante es
que el sistema operativo tiene mucho cuidado de asegurar que procesos independientes no puedan de
forma maliciosa o inadvertida afectar el comportamiento adecuado de los demás. En pocas palabras,
procesos múltiples puedan compartir concurrentemente el mismo CPU y otros recursos de hardware de
forma transparente. Usualmente, el sistema operativo requiere de hardware que permita esta
separación.
Esta transparencia de concurrencia tiene un alto costo. Por ejemplo, cada vez que un proceso es
creado, el sistema operativo debe crear un espacio de direcciones independiente y completo. La
asignación de este espacio de memoria puede requerir de la inicialización de segmentos de memoria,
por ejemplo, poner en ceros todo el segmento de datos, copiar el programa asociado en el segmento de
texto (instrucciones), y preparar un segmento de stack para datos temporales. También resulta costoso
el que el CPU realice un switch (cambio) de contexto (valores de registros, contador de programa,
Clase 04: Procesos
apuntador de stack, etc.) para cambiar de la ejecución de un proceso a otro. Aparte de grabar el
contexto del CPU, el sistema operativo debe modificar los registros de una Unidad de Administración de
Memoria (MMU) e invalidar cachés de traducción de direcciones, tales como el buffer de traducción de
direcciones virtuales (TLB – Translation Lookaside Buffer). Además, si el sistema operativo brinda
soporte a más procesos de los que pueden sostenerse simultáneamente en la memoria principal, éste
debe hacer un swap (cambio) de procesos entre la memoria principal y el disco antes de que se realice el
switch de contexto.
Un proceso sencillo puede contener varios programas ejecutables conocidos como threads
(hilos), que trabajan de manera conjunta como un todo coherente, cooperando entre sí (ver Figura 4.1).
En un programa o proceso, por ejemplo, un thread podría manejar señales de error, otro podrí enviar un
mensaje al usuario sobre ese error, mientras que un tercer thread podría ejecutar la tarea principal del
programa.
Figura 4.1. Diferentes esquemas de uso de threads en procesos.
Un thread resulta de la división de un programa en dos o más tareas que pueden correr
concurrentemente. Múltiples threads pueden existir dentro de un mismo proceso y, por lo tanto,
comparten entre sí los mismos recursos, tales como el espacio de memoria del proceso al que
pertenecen. Las diferencias principales entre procesos y threads son las siguientes:


Los procesos son típicamente independientes, mientras los threads existen como subconjuntos
de los procesos.
Los procesos contienen una cantidad considerable de información de estatus o contexto,
mientras los threads múltiples dentro de un proceso comparte este estatus al igual que la
memoria y otros recursos (ver Figura 4.2).
Clase 04: Procesos



Los procesos tienen espacios de direcciones separados entre sí, son independientes. Los threads
comparten el mismo espacio de direcciones (ver Figura 4.2).
Los procesos interactúan entre sí solo mediante mecanismos de comunicación interproceso
(IPC), lo cual se explicará con mayor detalle más adelante.
El cambio de contexto (context switching) entre threads en el mismo proceso es típicamente
más rápido que el cambio de contexto entre procesos. Al igual que un proceso, un thread
ejecuta su propia parte de código, independientemente de los otros threads. Sin embargo, en
contraste con un proceso, el thread no intenta obtener un alto grado de transparencia de
concurrencia si esto implica una degradación de desempeño. Por lo tanto, un sistema de threads
generalmente mantiene solo el mínimo de información para que un CPU pueda ser compartido
por varios threads. En particular, un contexto de thread comúnmente consiste simplemente de
un contexto de CPU y de información para la administración de threads.
Figura 4.2. Información de los contextos de un proceso y un thread (hilo).
Hay dos implicaciones importantes del uso de threads al momento de implementar una
aplicación. Primero que todo, el desempeño de una aplicación multithread difícilmente es peor que su
contraparte de thread sencillo (proceso simple). De hecho, en muchos casos, el uso de thread múltiples
permite obtener mejores niveles de desempeño. Segundo, ya que los threads no son protegidos
automáticamente unos de otros como en el caso de los procesos, el desarrollo de aplicaciones
multithread requiere de un esfuerzo intelectual adicional.
4.2 Niveles de Threads
Existen dos categorías básicas de threads:


Threads de Nivel-Usuario: implementados generalmente por medio de librerías de threads.
Threads de Nivel-Kernel: implementados mediante llamadas a sistema.
Clase 04: Procesos
De la combinación de ambas categorías se deriva una tercera conocida como

Threads de Nivel-Hibrido o Combinados.
La Figura 4.3 muestra el esquema de cada uno de estas tres categorías de threads.
Figura 4.3. (a) Threads Nivel-Usuario, (b) Threads Nivel-Kernel, (c) Threads Hibridos o Combinados.
Thread Nivel-Usuario (ULT)
En este nivel, el Kernel no tiene la noción de la existencia de los threads. Toda la administración
de los threads es realizada por la aplicación usando una librería de threads. El cambio de contexto de
threads no requiere de privilegios para el modo kernel (no hay cambio de modo) y la calendarización o
programación en tiempo depende específicamente de la aplicación.
Actividad del Kernel en threads de nivel-usuario:



El Kernel no sabe de la actividad de los threads pero aún administra la actividad de procesos.
Cuando un thread hace una llamada a sistema (system call), el proceso por completo será
bloqueado (*), pero para la librería de threads ese thread aún está corriendo (en estado de
correr - running).
Los estados de los threads son independientes de los estados de los procesos.
NOTA (*): Recuerde que un proceso puede estar en distintos estados dentro de su vida (mientras existe). Uno de estos estados
es el de “bloqueado”, lo cual significa que se encuentra en espera de que ocurra un evento específico para poder continuar su
procesamiento.
Clase 04: Procesos
Ventajas:



El cambio de contexto de los threads no involucra al kernel, no se requiere cambio de modo.
La calendarización puede ser específica a la aplicación, se puede seleccionar el mejor algoritmo.
Los threads nivel-usuario pueden correr en cualquier sistema operativo, solo se requiere una
librería de threads.
Desventajas:


La mayoría de las llamadas a sistema son de bloqueo y el Kernel bloquea a los procesos,
entonces todos los threads dentro del proceso también serán bloqueados.
El Kernel solo puede asignar procesos a procesadores, por lo que dos threads dentro del mismo
proceso no pueden correr simultáneamente en dos procesadores.
Thread Nivel-Kernel (KLT)
En este nivel, toda la administración de los threads se efectúa en el Kernel, no se usa librería de
threads pero sí debe existir llamadas a sistema dirigidas a los servicios de threads que provee el Kernel.
El Kernel mantiene la información de contexto tanto de procesos como de threads, y el cambio de
contexto de threads requiere que la calendarización que establece el Kernel sea basada en threads.
Ventajas:


El Kernel puede calendarizar simultáneamente varios threads del mismo proceso en varios
procesadores, el bloqueo se efectúa a nivel thread y no a nivel proceso.
Las rutinas del Kernel pueden ser multithread.
Desventajas:

El cambio de contexto entre threads del mismo proceso involucra al Kernel. Si se tienen dos
cambios de modo por cambio de contexto de switch, se aletarga la respuesta del sistema.
Thread Hibrido o Combinado
La idea es combinar lo mejor de cada categoría anterior.
El sistema operativo solaris, UNIX de Sun Microsystems, es un buen ejemplo de sistema
operativo que da soporte a threads hibridos.






La creación de threads tiene lugar en el espacio de usuario (modo de usuario).
Las tareas de calendarización y sincronización de threads se realiza en el espacio de usuario.
El programador puede ajustar el número de KLTs.
El Proceso incluye el espacio de direcciones de usuario, stack y bloqueo de control de proceso.
Los ULTs (librerías de threads) invisibles al sistema operativo son la interfaz para el paralelismo
de la aplicación.
Los KLTs son la unidad que puede ser despachada (asignada) a un procesador.
Clase 04: Procesos

Cada proceso de peso ligero (LWP – Lightweight Process) (**) soporta uno o más ULTs y es
mapeado a solo un KLT.
NOTA (**): Un proceso de peso ligero (LWP) es un tipo específico de thread nivel-kernel (KLT) que comparte el mismo estado e
información.Un LWP es un medio para implementar el multitasking. Un LWP corre arriba de un thread nivel-kernel sencillo y
comparte su espacio de direcciones y recursos de sistema con otros LWPs que pertenecen al mismo proceso. El LWP puede
generar múltiples threads de nivel-usuario (ULTs), permitiendo con ello el multitasking a nivel usuario, lo cual puede traer
algunos benecifios de desempeño.
La Figura 4.4 muestra lo anteriormente dicho.
Figura 4.4. Implementación de threads en el sistema operativo Solaris de Sun Microsystems.
4.3 Uso de procesos y threads en sistemas no distribuidos
Las aplicaciones grandes son frecuentemente desarrolladas como una colección de programas
que colaboran entre sí, y cada uno de los cuales es ejecutado por un proceso separado. Este método es
típico en un ambiente UNIX. La cooperación entre procesos es comúnmente implementada por medio
de mecanismos de comunicación interproceso (IPC). En los sistemas UNIX, estos mecanismos
típicamente incluyen pipas, filas de mensaje, segmentos de memoria compartida, sockets, RPC’s, etc.
Una de las principales desventajas de todos los mecanismos IPC es que la comunicación requiere
Clase 04: Procesos
frecuentemente de una cantidad considerable de cambios de contexto, los cuales se dan en tres puntos
diferentes, tal como se muestra en la Figura 4.5.
Figura 4.5. Cambio de contexto (context switching) como resultado de un IPC.
Ya que un IPC requiere de la intervención del kernel, un proceso generalmente tiene que cambiar de
modo de usuario a modo de kernel, lo cual se muestra en el punto S1 de la figura anterior. Esto requiere
cambiar el mapa de memoria en el MMU, y también desalojar el TLB. Dentro del kernel, se efectúa un
cambio de contexto de proceso (punto S2 en la gráfica anterior, caso ilustrado también en la Figura
4.6a), después de lo cual el otro proceso puede se activado al cambiar de modo kernel al modo usuario,
de nueva cuenta (punto S3). El último cambio requiere una vez más el cambiar el mapa del MMU y
desalojar el TLB.
En lugar de usar procesos, una aplicación puede ser construida de tal manera que partes
diferentes de la misma sean ejecutadas por threads separados. La comunicación entre estas partes se
implementa completamente con el uso de datos compartidos. El cambio de contexto de threads
(threads switching) puede hacerse frecuentemente en el espacio de usuario, aunque el kernel esté al
tanto de los threads y los controle y programe en el tiempo (ver Figura 4.6). El resultado puede ser una
mejora dramática en el desempeño.
Una ventaja de una granulación más fina, mediante la división de la aplicación en múltiples
threads (multithreading) es que hace posible el explotar el paralelismo cuando se ejecuta un programa
en un ambiente multitasking, y, aún más, en una máquina multiprocesador (existen sistemas operativos
que dan soporte a ambos ambientes, multitasking y multiprocesador a la vez). En el caso multitasking, el
tiempo de CPU es dividido entre threads y no entre procesos (ver Figura 4.6b). En el caso del sistema
multiprocesador, cada thread es asignado a un CPU diferente mientras que los datos compartidos son
almacenados en memoria principal compartida (ver Figura 4.6c). Cuando el diseño es apropiado, tal
Clase 04: Procesos
paralelismo puede ser transparente: el proceso correrá igualmente bien en un sistema uniprocesador,
aunque un poco más lento. El uso de multithreading para la implementación de paralelismo se ha vuelto
cada vez más importante y extendido, gracias a la disponibilidad de estaciones de trabajo
multiprocesador cuyos precios se han reducido significativamente. Estos sistemas de cómputo son
típicamente usados para correr servidores en aplicaciones cliente-servidor.
Figura 4.6. (a) Multitasking basado en procesos (procesos con un solo thread), (b) Multitasking basado
en threads (procesos con multithreads), (c) Procesos con multithreads en un ambiente multiprocesador.
Finalmente, hay una razón de la ingeniería de software para usar threads: muchas aplicaciones
son más sencillas de estructurar como una colección de threads cooperativos. Por ejemplo, en el caso de
un procesador de palabras (ver Figura 4.7), se pueden usar threads separados para manejar la entrada
de usuario en el teclado, revisar la ortografía y gramática, grabar el documento en disco, etc.
Clase 04: Procesos
Figura 4.7. Esquema de un procesador de palabras dividido en threads.
4.4 Uso de threads en sistemas distribuidos
A continuar …
Descargar