lunes, 18 de noviembre de 2013

Procesos

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.

 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. 



 Patrón de hilos Línea de ensamblado

 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:
  1. Carga
  2. Ejecución
  3. Terminación
Creación

  • Creació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.
    Carga
    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.
    Ejecución
    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.
    Terminación
    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()
    Estados de los Procesos

    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

    No hay comentarios:

    Publicar un comentario