Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
¿Qué es un hilo?
Un hilo es una unidad básica de utilización de CPU, la cual contiene un id de hilo,
su propio program counter, un conjunto de registros, y una pila; que se representa
a nivel del sistema operativo con una estructura llamada TCB (thread control
block).
Los hilos comparten con otros hilos que pertenecen al mismo proceso la sección
de código, la sección de datos, entre otras cosas. Si un proceso tiene múltiples
hilos, puede realizar más de una tarea a la vez (esto es real cuando se posee más
de un CPU).
Un Thread (Hilo) es una unidad básica de utilización de la CPU consistente en un
juego de registros y un espacio de pila. Es también conocido como proceso ligero.
Cada thread contendrá su propio program counter, un conjunto de registros, un
espacio para el stack y su prioridad.
Comparten el código, los datos y los recursos con sus hebras (thread) pares.
Una tarea (o proceso pesado) está formado ahora por uno o varios threads.
Un thread puede pertenecer a una sola tarea.
Todos los recursos, sección de código y datos son compartidos por los distintos
threads de un mismo proceso.
El hilo tiene un contador de programa que lleva el registro de cuál instrucción se
va a ejecutar a continuación. Tiene registros que contienen sus variables de
trabajo actuales. Tiene una pila, que contiene el historial de ejecución, con un
conjunto de valores para cada procedimiento al que se haya llamado, pero del cual
no se haya devuelto todavía. Aunque un hilo se debe ejecutar en cierto proceso, el
hilo y su proceso son conceptos distintos y pueden tratarse por separado. Los
procesos se utilizan para agrupar los recursos; son las entidades planificadas para
su ejecución en la CPU.
Los hilos comparten un espacio de direcciones y otros recursos; en el segundo,
los procesos comparten la memoria física, los discos, las impresoras y otros
recursos. Como los hilos tienen algunas de las propiedades de los procesos,
algunas veces se les llama procesos ligeros. El término multihilamiento también se
utiliza para describir la situación de permitir varios hilos en el mismo proceso.
Los distintos hilos en un proceso no son tan independientes como los procesos.
Todos los hilos tienen el mismo espacio de direcciones, lo cual significa que
también comparten las mismas variables globales. Como cada hilo puede acceder
a cada dirección de memoria dentro del espacio de direcciones del proceso, un
hilo puede leer, escribir o incluso borrar la pila de otro hilo.
Además de compartir un espacio de direcciones, todos los hilos pueden compartir
el mismo conjunto de archivos abiertos, procesos hijos, alarmas y señales, etc.
Al igual que un proceso tradicional (es decir, un proceso con sólo un hilo), un hilo
puede estar en uno de varios estados: en ejecución, bloqueado, listo o terminado.
Un hilo en ejecución tiene la CPU en un momento dado y está activo. Un hilo
bloqueado está esperando a que cierto evento lo desbloquee.
Es importante tener en cuenta que cada hilo tiene su propia pila, como la figura 2-
13 lo ilustra.
La pila de cada hilo contiene un conjunto de valores para cada procedimiento
llamado, pero del que todavía no se ha regresado. Este conjunto de valores
contiene las variables locales del procedimiento y la dirección de retorno que se
debe utilizar cuando haya terminado la llamada al procedimiento. Cada hilo llama
a distintos procedimientos y por ende, tiene un historial de ejecución diferente.
Esta es la razón por la cual cada hilo necesita su propia pila. Por ejemplo, si el
procedimiento X llama al procedimiento Y y Y llama al procedimiento Z, entonces
mientras se ejecuta Z, los conjuntos de valores para X, Y y Z estarán todos en la
pila.
Un thread (hilo de ejecución) es un programa en ejecución (flujo de ejecución)
que comparte la imagen de memoria y otras informaciones con otros procesos
ligeros. Un proceso puede contener un solo flujo de ejecución, como ocurre en
los procesos clásicos, o más de un flujo de ejecución. Desde el punto de vista de
la programación, cada thread se define como una función cuya ejecución se puede
lanzar en paralelo con otras.
El sistema operativo mantiene tablas de procesos y threads, dentro de
las que se almacena un bloque de control del proceso BCP por cada proceso o un
bloque de control del thread BCT por cada thread.
Procesos vs hilos
Semejanzas: Los hilos operan, en muchos sentidos, igual que los procesos.
Pueden estar en uno o varios estados: listo, bloqueado, en ejecución o terminado.
También comparten la CPU.
Sólo hay un hilo activo (en ejecución) en un instante dado.
Un hilo dentro de un proceso se ejecuta secuencialmente.
Cada hilo tiene su propia pila y contador de programa.
Pueden crear sus propios hilos hijos.
Diferencias: Los hilos, a diferencia de los procesos, no son independientes entre
sí.
Como todos los hilos pueden acceder a todas las direcciones de la tarea, un hilo
puede leer la pila de cualquier otro hilo o escribir sobre ella. Aunque pueda
parecer lo contrario la protección no es necesaria ya que el diseño de una tarea
con múltiples hilos tiene que ser un usuario único.
Ventajas: de los hilos sobre los procesos.
Se tarda mucho menos tiempo en crear un nuevo hilo en un proceso existente que
en crear un nuevo proceso.
Se tarda mucho menos tiempo en terminar un hilo que un proceso.
Se tarda mucho menos tiempo en conmutar entre hilos de un mismo proceso que
entre procesos.
Los hilos hacen más rápida la comunicación entre procesos, ya que al compartir
memoria y recursos, se pueden comunicar entre sí sin invocar el núcleo del SO.
Ventajas de usar hilos
• Respuesta: el tiempo de respuesta mejora, ya que el programa puede continuar
ejecutándose, aunque parte de él esté bloqueado.
• Compartir recursos: los hilos comparten la memoria y los recursos del proceso al
que pertenecen, por lo que se puede tener varios hilos de ejecución dentro del
mismo espacio de direcciones.
• Economía: Es más fácil la creación, cambio de contexto y gestión de hilos que de
procesos.
• Utilización múltiples CPU: permite que hilos de un mismo proceso ejecuten en
diferentes CPUs a la vez. En un proceso mono-hilo, un proceso ejecut
Diferencias entre hilos y procesos
Los hilos se distinguen de los tradicionales procesos en que los procesos son
generalmente independientes, llevan bastante información de estados, e
interactúan sólo a través de mecanismos de comunicación dados por el sistema.
Por otra parte, muchos hilos generalmente comparten otros recursos
directamente. En los sistemas operativos que proveen facilidades para los hilos,
es más rápido cambiar de un hilo a otro dentro del mismo proceso, que cambiar de
un proceso a otro. Este fenómeno se debe a que los hilos comparten datos y
espacios de direcciones, mientras que los procesos al ser independientes no lo
hacen. Al cambiar de un proceso a otro el sistema operativo (mediante el
dispacher) genera lo que se conoce como overhead, que es tiempo desperdiciado
por el procesador para realizar un cambio de modo (mode switch), en este caso
pasar del estado de Runnig al estado de Waiting o Bloqueado y colocar el nuevo
proceso en Running. En los hilos como pertenecen a un mismo proceso al realizar
un cambio de hilo este overhead es casi despreciable.
Para que se utilizan?
La principal razón de tener hilos es que en muchas aplicaciones se desarrollan
varias actividades a la vez. Algunas de ésas se pueden bloquear de vez en
cuando. Al descomponer una aplicación en varios hilos secuenciales que se
ejecutan en cuasi-paralelo, el modelo de programación se simplifica. Podemos
pensar en procesos paralelos. Sólo que ahora con los hilos agregamos un nuevo
elemento: la habilidad de las entidades en paralelo de compartir un espacio de
direcciones y todos sus datos entre ellas.
Un segundo argumento para tener hilos es que, como son más ligeros que los
procesos, son más fáciles de crear (es decir, rápidos) y destruir.
Una tercera razón de tener hilos es también un argumento relacionado con el
rendimiento. Los hilos no producen un aumento en el rendimiento cuando todos
ellos están ligados a la CPU, pero cuando hay una cantidad considerable de
cálculos y operaciones de E/S, al tener hilos estas actividades se pueden
traslapar, con lo cual se agiliza la velocidad de la aplicación.
Si el programa tuviera sólo un hilo, entonces cada vez que iniciara un respaldo en
el disco se ignorarían los comandos del teclado y el ratón hasta que se terminara
el respaldo. El usuario sin duda consideraría esto como un rendimiento pobre. Con
tres hilos, el modelo de programación es mucho más simple. El primer hilo
interactúa sólo con el usuario, el segundo proceso vuelve a dar formato al
documento cuando se le indica y el tercero escribe el contenido de la RAM al disco
en forma periódica. Debemos aclarar que aquí no funcionaría tener tres procesos
separados, ya que los tres hilos necesitan operar en el documento. Al tener tres
hilos en vez de tres procesos, comparten una memoria común y por ende todos
tienen acceso al documento que se está editando.
Estados de un Hilo
Los principales estados de un hilo son: ejecución, preparado y bloqueado; y hay
cuatro operaciones básicas relacionadas con el cambio de estado de los hilos:
Creación: En general, cuando se crea un nuevo proceso se crea también un hilo
para ese proceso. Posteriormente, ese hilo puede crear nuevos hilos dándoles un
puntero de instrucción y algunos argumentos. Ese hilo se colocará en la cola de
preparados.
Bloqueo: Cuando un hilo debe esperar por un suceso, se le bloquea guardando
sus registros. Así el procesador pasará a ejecutar otro hilo preparado.
Desbloqueo: Cuando se produce el suceso por el que un hilo se bloqueó pasa a la
cola de listos.
Terminación: Cuando un hilo finaliza, se liberan su contexto y sus pilas.
Un punto importante es la posibilidad de que el bloqueo de un hilo lleve al bloqueo
de todo el proceso. Es decir, que el bloqueo de un hilo lleve al bloqueo de todos
los hilos que lo componen, aun cuando el proceso está preparado.
Hilos a nivel usuario
Son implementados en alguna librería. Estos hilos se gestionan sin soporte del
SO, el cual solo reconoce un hilo de ejecución.
El primer método es colocar el paquete de hilos completamente en espacio de
usuario. El kernel no sabe nada acerca de ellos. En lo que al kernel concierne,
está administrando procesos ordinarios con un solo hilo. La primera ventaja, la
más obvia, es que un paquete de hilos de nivel usuario puede implementarse en
un sistema operativo que no acepte hilos. Todos los sistemas operativos solían
entrar en esta categoría e incluso hoy en día algunos todavía lo están. Con este
método, los hilos se implementan mediante una biblioteca.
Cuando los hilos se administran en espacio de usuario, cada proceso necesita su
propia tabla de hilos privada para llevar la cuenta de los hilos en ese proceso. Esta
tabla es similar a la tabla de procesos del kernel, excepto porque sólo lleva la
cuenta de las propiedades por cada hilo, como el contador de programa,
apuntador de pila, registros, estado, etc. La tabla de hilos es administrada por el
sistema en tiempo de ejecución. Cuando un hilo pasa al estado listo o bloqueado,
la información necesaria para reiniciarlo se almacena en la tabla de hilos, en la
misma forma exacta que el kernel almacena la información acerca de los procesos
en la tabla de procesos.
Los hilos de nivel usuario también tienen otras ventajas. Permiten que cada
proceso tenga su propio algoritmo de planificación personalizado.
En el nivel de usuario, es en donde se ejecutan casi todas las aplicaciones de un
sistema operativo actual. La manera de acceder a los dispositivos o servicios que
ofrece el sistema operativo, generalmente es mediante lo que se conoce como
System Call o llamada al sistema, para los amigos: syscall ;-). De esta forma se
logra un cierto control sobre los recursos que posee el sistema operativo, ya que
las aplicaciones de usuario sólo podrán acceder a los dispositivos o servicios del
sistema mediante estos procedimientos. El tema con estas llamadas al sistema es,
que muchas de ellas son bloqueantes, ¿y que quiere decir el término bloqueante?:
una vez que un proceso pide un recurso de entrada salida por ejemplo, en el caso
de llamar a fread() de C, todo el proceso pasa a estar bloqueado en espera de la
respuesta desde la controladora de la unidad de disco, mientras el planificador de
tareas (task scheduller) sede el procesador a otro proceso en la lista de espera.
Los ULT, están implementados desde una biblioteca creada a nivel de usuario que
se encarga de todos los aspectos de la gestión de los hilos. En este caso, el
sistema operativo no tiene por que conocer acerca de la existencia de los hilos en
un determinado proceso, ya que este mismo se encargará de gestionar el
funcionamiento de los hilos. La ventaja aquí es la no intervención del sistema
operativo en esta tarea, lo cual implica la no existencia del cambio de contexto, o
context switch (lo cual si existe para los KLT), esto reduce el overhead, pero a
cambio, si un ULT pide un recurso de entrada/salida mediante una syscall
bloqueante, hará que todo el proceso en donde este reside se bloquee también, ya
que el sistema operativo no sabe que existen los hilos dentro de este proceso, y lo
único que ve es que dicho proceso pide entrada salida, entonces lo bloquea por
completo hasta obtener una respuesta por parte del dispositivo. Existen técnicas
que permiten manejar el bloqueo a nivel proceso para poder gestionarlo
internamente a nivel hilo de usuario, una de estas técnicas se conoce como
Jacketing, básicamente consiste en no pedir de forma directa un recurso que
provocará el bloqueo del proceso. Los ULT utilizan en lo posible syscalls no
bloqueantes, las cuales se ejecutaran de forma asincronica, estas se manejan
mediante una pequeña rutina de consulta que verificará el estado del dispositivo
solicitado, y si este se encuentra ocupado, pasará el control a otro hilo en espera,
y cuando le llegue nuevamente el turno del hilo que solicitó acceso al dispositivo,
este repetirá nuevamente la consulta. Este procedimiento se llevará a cabo hasta
que el dispositivo haya terminado con lo que se le pidió.
Un aspecto importante es que los ULT no permiten aprovechar el
multiprocesamiento, es decir, no permiten la ejecución de dos o más ULT en
procesadores separados (en paralelo), esto se debe a que el Kernel del sistema
operativo que no conoce el manejo de Threads que realiza el proceso, asignará un
proceso distinto a cada procesador, y como los hilos viven dentro de los procesos,
no será posible llevar a cabo la paralelización.
Cualquier aplicación puede programarse para ser multihilo a través del uso de una
biblioteca de hilos, que es un paquete de rutinas para la gestión de ULT. La
biblioteca de hilos contiene código para la creación y destrucción de hilos, para
paso de mensajes y datos entre los hilos, para planificar la ejecución de los hilos, y
para guardar y restaurar el contexto de los hilos.
Por defecto, una aplicación comienza con un solo hilo y ejecutando en ese hilo.
Esta aplicación y su hilo se localizan en un solo proceso gestionado por el núcleo.
En cualquier momento que la aplicación esté ejecutando (el proceso está en
estado Ejecutando), la aplicación puede crear un nuevo hilo a ejecutar dentro del
mismo proceso. La creación se realiza llamando a la utilidad de creación en la bi-
blioteca de hilos. Se pasa el control a esta utilidad a través de una llamada a
procedimiento. La biblioteca de hilos crea una estructura de datos para el nuevo
hilo y luego pasa el control a uno de los hilos de ese proceso que esté en estado
listo, utilizando algún algoritmo de planificación. Cuando se pasa el control a la
biblioteca, se almacena el contexto del hilo actual, y cuando se pasa el control de
la biblioteca al hilo, se recupera el contexto de ese hilo. El contexto tiene
esencialmente el contenido de los registros del usuario, el contador que programa,
y los punteros de pila.
Toda la actividad descrita en el párrafo anterior tiene lugar en el espacio de
usuario y dentro de un solo proceso. El núcleo no es consciente de esta actividad.
El núcleo continúa planificando el proceso como una unidad y asigna al proceso
un único estado (Listo, Ejecutando, Bloqueado, etc.).
Desventaja:
Se produce una sobrecarga en lo que respecta a cantidad de operaciones
cuando se trabaja con este tipo de hilos, ya que constantemente se requiere
cambiar de modo, por ejemplo, cuando se quiere crear un hilo, se pasa del modo
usuario a modo Kernel y luego a modo usuario otra vez.
La principal desventaja del enfoque KLT en comparación con el enfoque ULT es
que la transferencia de control de un hilo a otro del mismo proceso requiere un
cambio de modo al núcleo. Para mostrar estas diferencias, la Tabla 4.1 muestra
los resultados de las medidas tomadas en un uniproce- sador VAX ejecutando un
sistema operativo tipo UNIX. Las dos medidas son las siguientes: Crear un
Proceso Nulo, el tiempo para crear, planificar, ejecutar, y completar un
proceso/hilo que llama al pro- cedimiento nulo (es decir, la sobrecarga de crear un
proceso/hilo); y Señalizar-Esperar, el tiempo que le lleva a un proceso/hilo
señalizar a un proceso/hilo que está esperando y a continuación esperar una
condición (es decir, la sobrecarga de sincronizar dos procesos/hilos).