Generalidades acerca de los hilos o procesos ligeros Un proceso es

Anuncio
Generalidades acerca de los hilos o procesos ligeros
Un proceso es un programa en ejecución lo que significa que el proceso
incluye los valores actuales del contador de programa, los registros de la
CPU, las tablas de asignación de memoria y muchas otras cosas, en fin todo
lo que permita que pueda ser interrumpido en un punto dado para después
reanudar la ejecución.
Toda la magia de interrumpir el proceso y poder continuar su ejecución
posteriormente se apoya en la estructura de datos conocida como PCB (es un
nombre genérico no tiene que llamarse así en todos los SO). Los hilos basan su
idea en la misma concepción lo cual se refleja claramente en su nombre
alternativo de procesos ligeros en contraposición de los procesos pesados
que se crean cuando se ordena ejecutar un programa o se bifurca con una
llamada al sistema para crear proceso (pesados) como la llamada al sistema
fork de los sistemas operativo de la familia Unix.
Los procesos pesados o simplemente procesos no comparten nada y la única
forma de comunicarse entre sí es a través de paso de mensajes o por otras
técnicas tales como las tuberías.
El modelo de proceso posee un hilo simple de control de proceso mientras el
modelo de hilo permite tener más de un hilo de control.
Hilos
Existen diversos programas para mejorar la velocidad con que se bajan los
documentos de Internet, por ejemplo el Internet Download Manager, el Teleport
(es específico para páginas Web), etc. La idea básica de esos programas es
correr distintas instancias de ellos bajando diferentes partes del documento,
cada instancia es un hilo que tiene la misma funcionalidad que las restantes
pero actúa sobre datos diferentes. Los sistemas operativos modernos contienen
funciones específicas para el trabajo con hilos.
Un hilo es, básicamente, una unidad de utilización de la CPU y desde ese punto
de vista tiene la misma estructura que un proceso y el SO debe usar un PCB
para controlarlo.
En la figura 1 se puede apreciar la diferencia entre un proceso y un hilo, observe
que:
•
en el modelo de proceso el código los datos, la tabla de archivos abiertos
los registros y la pila (en fin todo) son accedidos por un solo hilo de
control de ejecución,
•
en el modelo de hilo existe un segmento de código, uno de datos y una
tabla de archivos abiertos común pero cada hilo tiene sus propios
registros y pila.
código
datos
registros
archivos
pila
código
registros
pila
datos
archivos
registros
pila
registros
pila
hilo
hilo
Figura 1 Modelo de proceso y de hilo.
Beneficio de los hilos
Los beneficios de la programación multihilo se pueden ver desde cuatro aristas:
1. Mejor respuesta. Una aplicación multihilo permite que el proceso continúe
ejecutando aún cuando alguna de sus partes está bloqueada o se
encuentra resolviendo una operación muy lenta. Por ejemplo un browser
multihilo puede permitir que el usuario interactúe con ella (a través de un
hilo) mientras esta cargando una imagen (usando otro hilo).
2. Compartir recursos. Por defecto los hilos comparten la memoria y los
recursos del proceso al cual pertenecen, lo que da por resultado que la
aplicación pueda tener varios hilos en actividad dentro del mismo espacio
de direcciones.
3. Economía. Asignar memoria y recursos para crear procesos es una
actividad costosa. Debido a que los hilos comparten los recursos del
proceso al cual pertenecen, resulta más económica su creación y el
cambio de contexto (pasar el procesador de un hilo a otro).
4. Utilización de las arquitecturas de multiprocesadores. Los beneficios que
da la programación con hilos puede incrementarse considerablemente en
un sistema con más de un procesador ya que los hilos pueden ejecutar en
paralelo de forma real.
Muchos SO modernos usan el concepto de hilo, por ejemplo Solaris crea un
conjunto de hilos en el kernel para controlar las interrupciones, Linux usa un hilo
del kernel para controlar la cantidad de memoria, etc.
Modelos multihilos.
El soporte para hilo puede darse a nivel de usuario (hilos de usuario) o puede
ser dado por el kernel (hilos de kernel). Los primeros se soportan arriba del
kernel y por ende éste no lo apoya. Mientras los segundos son manejados
directamente por el SO. Casi todos los SO modernos incluyen soporte para hilos
a nivel de kernel, por ejemplo: Windows XP, Linux, Mac OS X, Solaris, entre
otros.
Debe existir una forma de establecer una relación entre los hilos de usuario y los
hilos del kernel, seguidamente se detallan tres de ellas:
1. Modelo “muchos a uno”. En este modelo (figura 2) muchos hilos de nivel
de usuario se mapean en un hilo kernel. La manipulación de los hilos la
hace una biblioteca que reside en el espacio de usuario de forma que es
eficiente, pero, si un hilo hace una llamada al sistema de bloqueo, el
proceso entero se bloquea. Además debido a que solo un hilo puede
acceder al kernel en un instante de tiempo dado, no se pueden correr
procesos paralelos en sistemas con multiprocesadores.
Hilo de usuario
Hilo de kernel
k
Figura 2. Modelo muchos a uno.
2. Modelo “uno a uno”. Este modelo (figura 3) mapea cada hilo de usuario
en un hilo del kernel, dando una mayor concurrencia que el modelo
anterior y permite que un hilo corra cuando otro hace una llamada de
bloqueo, además los hilos pueden correr en diferentes procesadores
cuando se usan sobre un sistema de multiprocesamiento. El único
problema es que la creación de un hilo de usuario necesita la creación de
un hilo kernel. Linux y los SO Windows (95, 98, NT, 2000 y XP)
implementan este modelo.
Hilo de usuario
k
k
k
k
Hilo de kernel
Figura 3. Modelo uno a uno.
3. Modelo “muchos a muchos”. En este caso (figura 4) se multiplexan
muchos hilos de nivel de usuario en una cantidad menor o igual de hilos
kernel. La cantidad de hilos kernel puede especificarse para una
aplicación particular o para una máquina particular. Los desarrolladores
pueden crear tantos hilos como necesiten y el correspondiente hilo kernel
puede ejecutar en una arquitectura paralela.
Hilo de usuario
k
k
k
Hilo de kernel
Figura 4. Modelo muchos a muchos.
Hilos y bibliotecas de hilos
Para clarificar la idea de los hilos, analice el siguiente ejemplo:
•
un grupo de estudiantes trabajan en un laboratorio de computación y
todos hacen algún acceso a un servidor Web dado. Esta situación genera
una buena cantidad de solicitudes que el servidor debe satisfacer,
o ¿qué sucedería si una aplicación con un solo hilo de control fuera
la responsable de satisfacer todas esas solicitudes?
Queda claro que la cola de espera sería muy grande y la prestación de
servicios muy mala, ahora imaginen el problema a nivel global, por
ejemplo el acceso a Google que diariamente hacen muchas personas
en el mundo .
El modelo de hilo resuelve parte de ese problema al permitir que para cada
petición se cree un hilo que “escucha” y atienda las solicitudes.
Bibliotecas de hilos.
Las bibliotecas de hilos proveen al programador una API para crear y manejar
hilos, existen dos aproximaciones al problema.
•
la biblioteca está en el espacio de usuario y por tanto una invocación a
una de sus funciones es una llamada a una función local dentro del
espacio de memoria del usuario.
•
La biblioteca está implementada directamente en el kernel del SO y por
tanto una llamada a una de sus funciones es una llamada al sistema en el
espacio del kernel.
Existen tres bibliotecas importantes que se apoyan en esta ideas: Pthreads,
Win32 y Java.
Hilos con Pthread.
Pthread es una API para la creación y sincronización de hilos. En realidad es
una especificación para el comportamiento de hilos (según el estándar POSIX
IEEE 1003.1) y no una implementación, de manera que los diseñadores de
sistemas operativos tienen que implementarla de la forma en que deseen.
Existen muchos SO que la implantan, por ejemplo: Solaris, Linux, Mac OS X.
Existen, además, implementaciones shareware disponibles y de dominio público
para varias implantaciones de Windows.
Observe, en el ejemplo que sigue a estos comentarios, el uso de la API Pthread
para realizar una sumatoria.
En un programa PThreads los hilos comienzan su ejecución en una función
específica que en este caso es la función runner.
Obsérvese que
la variable sobre la que se realiza la suma -sum- es una
variable compartida por todos los hilos.
El programa comienza la ejecución, con un solo hilo de control, en la función
main, en la línea pthread_create(&tid, &attr, runner, argv[1]); se crea el hilo,
las parámetros que se le pasan a la función son el identificador del hilo (tid), su
conjunto de atributos (attr), el nombre de la función que debe invocarse y el
argumento que se le pasó al proceso original (argv[1]). En ese momento el
proceso tiene dos hilos, el proceso padre y el que se acaba de crear que es el
que realizará la suma. El hijo realizará la suma en la función runner y el padre
ejecuta la función pthread_join para esperar por su hijo e imprimir el resultado
de la suma.
#include <pthread.h>
#include <stdio.h>
int sum;
void *runner(void *param);
/* variable compartida por los hilos */
/* el hilo */
int main(int argc, char *argv[ ])
{
pthread_t tid;
/* identificador del hilo */
pthread_attr_t attr; /*conjunto de atributos del hilo */
if (argc != 2)
{
fprintf(stderr, “uso: a.out <integer value>\n”);
return -1;
}
if (atoi(argv[1] < 0)
{
fprintf(stderr, “%d debe ser >= 0\n”, atoi(argv[1]);
return -1;
}
/* obtener los atributos por defecto */
pthread_attr_init(&attr);
/* crear el hilo */
pthread_create(&tid, &attr, runner, argv[1]);
/* esperar por el hilo */
pthread_join(tid, NULL);
printf(“la suma es %d\n”, sum);
}
void *runner(void *param)
{
int, upper = ator(param);
sum = 0;
for(i = 1; i <=upper; i++)
sum +=I;
pthread.exit(0);
}
Hilos con Win32
La técnica usada en este caso es similar a la anterior en varios sentidos,
observe el programa siguiente que ilustra las ideas.
Al igual que que la versión anterior, en la que se uso Pthreads, los datos que se
comparten por los hilos (Sum en este caso) se declaran globales (el tipo de dato
DWORD es un entero sin signo de 32 bits).
También se define la función Summatation() la que es usada por hilos
separados, a la cual se le pasa un puntero a void que es definido en Win32
como LPVOID, los hilos que usan esta función ponen en la variable global Sum
el valor de la suma desde 0 hasta el parámetro pasado a ella.
Los hilos en Win32 se crean usando la función CreateThread(), la cual recibe un
conjunto de atributos tales como: información de seguridad, la longitud de la pila
y una bandera que puede iniciarse para indicar si el hilo va a comenzar en un
estado suspendido. Como en el ejemplo no se ha indicado eso, el proceso es
elegible por el planificador de periodo corto.
Una vez que se crea el hilo de suma, el padre debe esperar la terminación del
hijo para lo cual usa la función WaitForSingleObject(). El programa Phtread del
ejemplo anterior usa para esos fines la sentencia pthread_join()..
#include <Windows.h>
#include <stdio.h>
DWORD Sum; /* Datos compartidos por los hilos*/
DWORD WINAPI Summation(LPVOID Parm)
{
DWORD Upper = *(DWORD*) Param;
for (DWORD i = 0; i <= Upper; i++)
Sum += i;
return 0;
}
int mai(int argc, char *argv[])
{
DWORD ThreaID ;
Handle ThreadHandle ;
int param ;
if(argc != 2)
{
fprintf(stderr, “Se necesita un entero\n“ ) ;
return -1
}
Param = atoi(argv[1]);
if(Param < 0)
{
fprintf(stderr, “Se necesita un entero mayor o igual a cero\n) ;
return -1¨
}
//Crear el hilo
ThreadHandle = CreateThread(
NULL,
//Atributos de seguridad por defecto
Sumation, //Función hilo (thread)
&Param, //Parámetros de la función hilo
0,
//Banderas por defecto
ThreadId, // Identificador de hilo retornado)
if(ThreadHandle != NULL)
//Espera por la terminación del hilo
WaitForSingleObject(ThreadHandle, INFINITE);
CloseHandle(ThreadHandle);
printf(“la suma es %d\n”, sum);
}
Hilos en Java
La tercera variante que mencionamos es usar los hilos desde un lenguaje de alto
nivel que proporciona esa facilidad, el lenguaje Java posee una biblioteca con
ese fin, no entraremos en detalles de ese caso.
Las llamadas al sistema fork y exec en ambiente multihilo
Cuando se hace una llamada al sistema fork, se crea una copia exacta (el hijo)
del proceso que la invoca el cual pasa a competir individualmente por la CPU sin
compartir nada con el proceso que lo creó (el padre). El hijo solo hereda un
conjunto de recursos ya inicializados (archivos abiertos, los valores de las
variables, etc.) pero todo es una copia del original en una zona de memoria
diferente.
Por otra parte una llamada al sistema exec, sustituye el código actual por el
código de algún programa que se pasa como argumento.
En algunos sistemas UNIX la llamada al sistema fork en un ambiente multihilo
tiene dos versiones:
• En el primer caso se duplican todos los hilos del proceso que la invoca.
• En el segundo caso solo se duplica el hilo que la invocó.
Descargar