Procesos e hilos
Como
vimos, la cantidad de información que el sistema operativo debe manejar acerca
de cada proceso es bastante significativa. Si cada vez que el planificador
elige qué proceso pasar de Listo a En ejecución debe considerar
buena parte de dicha información, la simple transferencia de todo esto entre la
memoria y el CPU podría llevar a un desperdicio burocrático de recursos.
Una respuesta a esta problemática fue la de los hilos de ejecución, a
veces conocidos como procesos ligeros (Lightweight processes, LWP).
Cuando
consideramos procesos basados en un modelo de hilos, podríamos proyectar en
sentido inverso que todo proceso es como un sólo hilo de ejecución. Un sistema
operativo que no ofreciera soporte expreso a los hilos lo agendaría exactamente
del mismo modo.
Pero
visto desde la perspectiva del proceso hay una gran diferencia: Si bien el
sistema operativo se encarga de que cada proceso tenga una visión de virtual
exclusividad sobre la computadora, todos los hilos de un proceso comparten un
sólo espacio de direccionamiento en memoria y lista de descriptores de archivos
y dispositivos abiertos. Cada uno de los hilos se ejecuta de forma
(aparentemente) secuencial y maneja su propio contador de programa (y algunas
estructuras adicionales, aunque mucho más ligeras que el PCB).
Los hilos y el sistema operativo
Formalmente,
una programación basada en hilos puede hacerse completamente y de forma
transparente en espacio de usuario (sin involucrar al sistema operativo). Estos
hilos se llaman hilos de usuario (user threads), y muchos
lenguajes de programación los denominan hilos verdes (green threads).
Un caso de uso interesante es en sistemas operativos mínimos (p.ej. para
dispositivos embebidos) capaces de ejecutar una máquina virtual de alguno de
estos lenguajes: Si bien el sistema operativo no maneja multiprocesamiento, a
través de los hilos de usuario sí podemos crear procesos con multitarea
interna.
Los
procesos que implementan hilos ganan un poco en el rendimiento gracias a no
tener que reemplazar al PCB activo, pero además de esto, ganan mucho más por la
ventaja de compartir espacio de memoria sin tener que establecerlo
explícitamente a través de mecanismos de comunicación entre procesos (IPC
— Inter Process Communications). Dependiendo de la plataforma, a veces
los hilos de usuario inclusive utilizan multitarea cooperativa para pasar el
control dentro de un mismo proceso. Cualquier llamada al sistema bloqueante
(como obtener datos de un archivo para utilizarlos inmediatamente) interrumpirá
la ejecución de todos los hilos, dado que el control de ejecución es entregado
al sistema operativo.
El
siguiente paso fue la creación de hilos informando al sistema operativo,
típicamente denominados hilos de kernel (kernel threads). A
través de bibliotecas de sistema que los implementan de forma estándar para los
diferentes sistemas operativos (p.ej. pthreads para POSIX o Win32_Thread para Windows) o
arquitecturas. Estas bibliotecas aprovechan la comunicación con el sistema
operativo tanto para solicitudes de recursos (p.ej. un proceso basado en hilos
puede beneficiarse de una ejecución verdaderamente paralela en sistemas
multiprocesador) como para una gestión de recursos más comparable con una
situación de multiproceso estándar.
pthreads para POSIX o Win32_Thread para Windows) o
arquitecturas. Estas bibliotecas aprovechan la comunicación con el sistema
operativo tanto para solicitudes de recursos (p.ej. un proceso basado en hilos
puede beneficiarse de una ejecución verdaderamente paralela en sistemas
multiprocesador) como para una gestión de recursos más comparable con una
situación de multiproceso estándar. Patrones de trabajo con hilos
Hay
tres patrones en los que caen generalmente los modelos de hilos; podemos
emplear a más de uno de estos patrones en diferentes áreas de nuestra
aplicación, e incluso podemos anidarlos (esto es, podríamos tener una línea
de ensamblado dentro de la cual uno de los pasos sea un equipo de
trabajo):
Jefe
/ trabajador
Un hilo tiene una tarea distinta de
todos los demás: El hilo jefe genera o recopila tareas que requieren ser
cubiertas, las separa y se las entrega a los hilos trabajadores.
Este modelo es el más común para
procesos que implementan servidores (es el modelo clásico del servidor Web Apache)
y para aplicaciones gráficas (GUIs), en que hay una porción del programa (el
hilo jefe) esperando a que ocurran eventos externos. El jefe realiza
poco trabajo, se limita a invocar a los trabajadores a hacer el trabajo de
verdad; como mucho, puede llevar contabilidad de los trabajos realizados.
Típicamente, los hilos realizan su
operación, posiblemente notifican al jefe de su trabajo, y finalizan
su ejecución.
| Patrón de hilos jefe/trabajador |
Fuente: sistop.gwolf.org/html/02_administracion_de_procesos.html
Equipo
de trabajo
Al iniciar la porción multihilos del
proceso, se crean muchos hilos idénticos, que realizarán las mismas tareas
sobre diferentes datos. Este modelo es muy frecuentemente utilizado para
cálculos matemáticos (p.ej. criptografía, render). Puede combinarse con un
estilo jefe/trabajador para irle dando al usuario una previsualización del
resultado de su cálculo, dado que éste se irá ensamblando progresivamente,
pedazo por pedazo.
Sus principales diferencias con el
patrón jefe/trabajador consisten en que el trabajo a realizar por cada
uno de los hilos se plantea en un principio, esto es, el paso de división de
trabajo no es un hilo más, sino que prepara los datos para que éstos sean
lanzados en paralelo. Estos datos no son resultado de eventos independientes
(como en el caso anterior), sino que partes de un sólo cálculo.
Por
consecuencia, resulta natural que en este modelo los resultados generados por
los diferentes hilos son agregados o totalizados al terminar su
procesamiento. Los hilos no terminan, sino que son esperados y
continúa la ejecución lineal.
| Patrón de hilos Equipo de trabajo Fuente: sistop.gwolf.org/html/02_administracion_de_procesos.html |
Línea
de ensamblado
Si una tarea larga puede dividirse en
pasos sobre bloques de la información total a procesar, cada hilo puede
enfocarse a hacer sólo una tarea y pasarle los datos a otro hilo conforme vaya
terminando. Una de las principales ventajas de este modelo es que nos ayuda a
mantener rutinas simples de comprender, y permite que el procesamiento de datos
continúe incluso si parte del programa está bloqueado esperando E/S.
Un punto importante a tener en cuenta
en una línea de ensamblado es que, si bien los hilos trabajan de forma
secuencial, pueden estar ejecutándose paralelamente sobre bloques consecutivos
de información, eventos, etc.
Este patrón es claramente distinto de
los dos anteriormente presentados; si bien en los anteriores los diferentes
hilos (a excepción del hilo jefe) eran casi siempre idénticos, aunque
operando sobre distintos conjuntos de datos, en este caso son completamente
distintos.
Fuente: sistop.gwolf.org/html/02_administracion_de_procesos.html
Concurre
Formalmente
y desde las ciencias de la computación, concurrencia no necesariamente
se refiere a dos o más eventos que ocurran a la vez, sino que a dos o más
eventos cuyo órden es no determinista, esto es, eventos acerca de los
cuales no podemos predecir el órden relativo en que ocurrirán. Esto
puede ocurrir porque hablamos de dos hilos ejecutándose en conjunto, dos
procesos independientes en el mismo equipo, o incluso procesos independientes
en computadoras separadas geográficamente; el estudio de situaciones derivadas
de la concurrencia es uno de los campos de estudio clásico (y más abstracto) de
las ciencias de la computación.
Si
bien una de las tareas principales de los sistemas operativos es dar a cada
proceso la ilusión de que se está ejecutando en una computadora dedicada, de
modo que el programador no tenga que pensar en la competencia por recursos, a
veces esta ilusión sencillamente no puede presentarse — Parte del desarrollo de
un programa puede depender de datos obtenidos en fuentes externas a éste, y la
cooperación con hilos o procesos externos es fundamental.
Para
algunos de los ejemplos a continuación, presentaremos ejemplos usando la
semántica de la interacción entre hilos del mismo proceso, sincronización entre
procesos independientes, asignación de recursos por parte del núcleo a procesos
simultáneos, o incluso entre usuarios de diferentes equipos de una red — En
todos estos casos, los conceptos presentados pueden generalizarse a los demás,
y son situaciones en que se presenta compartición (o competencia) por
estructuras entre entes independientes.
Fuente: sistop.gwolf.org/html/02_administracion_de_procesos.html
Ciclo de vida de un proceso
Un
proceso pasa por cuatro fases:
- Carga
- Ejecución
- Terminación
Crear un
proceso consiste en asignar un proceso ID al nuevo proceso y configurar la
información que define el entorno de trabajo del nuevo proceso. La mayor parte
de esta información es heredad del proceso padre.
La carga
de un proceso se realiza mediante un loader thread (hilo cargador). El
código que efectúa la carga reside en el Administrador de Procesos, sin
embargo, el thread se ejecuta bajo el proceso ID del nuevo proceso. Esto
permite al Administrador de Procesos dedicarse a otras tareas mientras se
cargan los programas.
Una vez
el código del programa ha sido cargado, el proceso está listo para ser ejecutado;
comienza a competir con otros procesos por los recursos de la CPU.
Un
proceso puede finalizar su tarea de dos formas:
- emisión de una señal cuya
acción definida es causar la terminación de un proceso
- el proceso hace una llamada exit()
Un
proceso se encuentra siempre en uno de los siguientes estados:
- READY - el proceso puede
hacer uso de la CPU (no está esperando la ocurrencia de ningún evento).
- BLOCKED - el proceso está en
un estado de bloqueo, que puede ser:
- SEND-blocked
- RECEIVE-blocked
- REPLY-blocked
- SIGNAL-blocked
- HELD - el proceso ha
recibido una señal de HOLD. Mientras no salga de este estado, el proceso
no puede utilizar la CPU; el único modo de sacarlo del estado HELD es
recibir otra señal que, o bien le permita seguir con su ejecución, o bien,
lleve al proceso a su terminación.
- WAIT-blocked - el proceso ha
hecho una llamada wait() o waitpid() y ha de esperar por el
estatus de uno o más de sus procesos hijos.
- DEAD - el proceso ha
terminado pero no puede enviar su estatus de exit a su padre porque éste
no ha realizado una llamada wait() o waitpid(). Un proceso
DEAD tiene un estado, pero la memoria que antes ocupaba ha sido liberada.
Un proceso en este estado también se denomina proceso zombie.
Fuente: www.iuma.ulpgc.es/~avega/int_equipos/trab9899/qnx/cap3.htm
- SEND-blocked
- RECEIVE-blocked
- REPLY-blocked
- SIGNAL-blocked
Fuente: www.iuma.ulpgc.es/~avega/int_equipos/trab9899/qnx/cap3.htm
No hay comentarios:
Publicar un comentario