HILOS

Uno de los pasos importantes de la informática dio a favor de los desarrolladores de software fue colocar un nivel de software por encima del hardware de un ordenador. Este nivel de software, conocido como sistema operativo, es en esencia una interfaz fácil de utilizar que nos permite controlar todas las partes del hardware en la mayoría de los casos, sin un profundo conocimiento del mismo. A su vez, los sistemas operativos también han experimentado un gran avance, pasando de los sistemas de un único procesador a los actuales sistemas operativos distribuido o de red, o a los sistemas operativos con multiprocesadores. El concepto central de cualquier sistema operativo es el de proceso. Cualquier ordenador hoy en día es capaz de hacer varias cosas simultáneamente. De esta forma, todos los programas, incluyendo los que componen el sistema operativo, que tengan que ejecutarse simultáneamente (multiprogramación) se organizan en varios procesos secuenciales.

CONCEPTO DE PROCESO
Un proceso es un ejemplar en ejecución de un programa. Cada proceso consta de bloques de código y datos cargados desde un fichero ejecutable o desde una biblioteca dinámica. También es propietario de otros recursos que se crean durante la vida de dicho proceso y se destruyen cuando finaliza. Por ejemplo un proceso posee: Su propio espacio de direcciones Su memoria Sus variables Ficheros abiertos Procesos hijo Contador de programa, registros, pila, señales, semáforos, etc.

Lo anterior es equivalente a decir que cada proceso tiene su propia UCP virtual, lo que nos permite comprender mejor como un sistema puede ejecutarse varios procesos simultáneamente, aunque la realidad sea que la UCP alterna entre esos procesos. Según lo expuesto será un error confundir un programa con un proceso. Para evitar este posible malentendido, considere el siguiente ejemplo: cuando instalamos un juego en nuestro ordenador lo hacemos siguiendo las instrucciones adjuntas. En este caso, las instrucciones serían el programa, la actividad que hay que desarrollar para realizar la instalación (leer las instrucciones, introducir el CDROM, etc.) el proceso y nosotros la UCP. De lo anterior se deduce que un proceso puede estar en ejecución (está utilizando la UCP) preparado (está detenido temporalmente para que se ejecute otro proceso) o en espera (no se puede ejecutar debido a que ocurrió algún evento al que hay que responder adecuadamente). En estos tres estados son posibles, como en la figura siguiente, cuatro transiciones:

y si no. si el proceso está en espera pasará a preparado cuando se dé el evento externo por el que espera y si está preparado. semáforos. Según se ha expuesto en el apartado anterior. es en el que estamos acostumbrados a trabajar habitualmente. estaríamos en el caso de múltiples hilos ejecutándose concurrentemente (multi-threading). varios cajeros pueden actuar simultáneamente. Esto pone de manifiesto que un proceso no se ejecuta. cada hilo se ejecuta de forma estrictamente secuencial y tiene su propia pila. por lo que el lanzamiento y la ejecución de un proceso. no sucediendo lo mismo con los hilos soportados por una aplicación (en Windows NT todos los hilos son soportados por el Kernel) . para al estado de espera o también. pasa a ejecución cuando el planificador lo decida por que los demás procesos ya han tenido su tiempo de UCP. comparten el mismo espacio de direcciones. Tales conceptos son parte del estudio de sistemas operativos. pasa al estado de preparado. En cambio si el programa permitiera lanzar un hilo por cada petición de un cajero para actualizar una cuenta. No obstante.Si un proceso en ejecución no puede continuar. habrá situaciones en las que la mejor solución para ayudar en el trabajo sea crear un nuevo proceso (proceso hijo). En este caso se utilizan distintos mecanismos para la sincronización y comunicación entre procesos. considere un programa que incluya la siguiente secuencia de operaciones para actualizar el saldo de una cuenta bancaria cuando se efectúa un nuevo ingreso saldo= Cuenta. pueden crear hilos hijo y se pueden bloquear. Los hilos comparten un espacio de memoria. en él. En cambio. crear hilos adicionales. el estado de los registros de la UCP y su propio contado de programa. el código y los recursos. en el caso de hilos soportados por el Kernel. en un sistema operativo tradicional. al mismo tiempo. continuando con el ejemplo anterior piense en un banco real. Cuando se crea un proceso. No obstante. Los hilos comparten la UCP de la misma forma que lo hacen los procesos. Como ya hemos indicado. saldo += ingreso. En la UCP puede haber varios programas con varios procesos ejecutándose concurrentemente. el mismo conjunto de ficheros abiertos.ObtenerSaldo( saldo). etc. Pero. procesos hijos (no hilos hijos) señales. cada proceso tiene un espacio de direcciones y un único hilo de control. Por otra parte. el sistema operativo cera su primer hilo (hilo primario) el cual puede a su vez. lo que significa compartir también las mismas variables globales. HILOS Un hilo (thread-llamado también proceso ligero o subproceso) es la unidad de ejecución de un proceso y está asociado con una secuencia de instrucciones. visualizar documentos y traer ficheros de internet. piense como escribiría un programa con un solo hilo de control para mostrar animación. Este modelo de programación en el que se ejecuta un solo hilo. sonido. si puede continuar y el planificador decide que ya ha sido ejecutado el tiempo suficiente. muchos problemas pueden ser resueltos mejor con múltiples hilos. un conjunto de registros y una pila. Por ejemplo.ObtenerSaldo().Está característica ya es una realidad en los sistemas operativos modernos de hoy y como consecuencia contemplada en los lenguajes de programación actuales. Cuenta. sino que es sólo el espacio de direcciones donde reside el código que es ejecutado mediante uno o más hilos. mientras un hilo esté bloqueado se puede ejecutar otro hilo del mismo proceso. Ejecutar el mismo programa por cada uno de los cajeros tiene un costo elevado (recuerde los recursos que necesita).

Que se crean mediante llamadas o funciones de la interfaz de programación de aplicaciones (API) como por ejemplo. supongamos el diseño de una aplicación procesador de texto ¿Será acertado crear un hilo separado para manipular cualquier tarea de impresión? Esto permitiría al usuario continuar utilizando la aplicación mientras se está imprimiendo. LPSTARTUPINFO1pInfoInicial. Cuando Windows NT crea un objeto devuelve un descriptor a la aplicación para poder identificar al objeto. LPVOID 1pEntorno. quizás creando un fichero temporal que contenga los datos a imprimir. Por ejemplo. En cambio ¿Qué sucederá si los daos del documento cambian mientras se imprime? Esto es un problema que hay que resolver. Crear un Proceso Windows NT gestiona los procesos y los hilos como objetos. CreateProcess.Estados de Un Hilo Como los procesos con un solo hilo de control. LPCTSTR 1pArgsLinea0r LPSECURITY_ATTRIBUTES 1pAtributosProceso. el sistema operativo crea un hilo primario. Para muchos procesos esté es el último hilo necesario. De esta forma. . sucesos. porque aunque los conceptos no difieren de un sistema operativo a otro. vamos a centrarnos en un sistema operativo como Windows NT. Además de los objetos procesos y los objetos hilo. Cuando un objeto de Windows NT ya existe. dormido. la aplicación puede pasar a ese objeto a cualquier otra función que necesite manipulación. cualquier otra aplicación puede abrirlo. Terminado: el hilo ha finalizado pero todavía no ha sido recogido por su padre. el hilo está esperando ser activado. si hay diferencia en la interfaz de programación utilizada por cada una de ellas. En ejecución: el hilo está activo y le ha sido asignada la UCP(solo los hilos activados pueden ser ejecutados) Bloqueado: El hilo espera que otro elimine el bloqueo. hay otros tipos de objetos como. DWORD dwIndicadores. Cuando se debe crear un Hilo Cada vez que se crea un proceso. PROCESOS E HILOS Para el estudio de la creación. por ejemplo. Sin embargo un proceso puede crear otros hilos para ayudarse en su trabajo. esto significa que el sistema no crea un nuevo objeto para esa aplicación. etc. utilizando UCP al máximo posible. LPSECURITY_ATTRIBUTES 1pAtributosHilo. Un proceso se crea cuando se invoca a la función CreateProcess: BOOL CreateProcess( LPCTSTR 1pNombreAp. LPCTSTR 1pDirectorioActual. asignación de ficheros. simplemente utiliza la existente y deja constancia de ello incrementando un contador de uso del objeto. semáforos. LPPROCESS_INFORMATION 1pInfoProceso ). exclusión mutua (exmut). BOOL bDescriptorHeredable. los hilos pueden encontrarse en uno de los siguientes estados: Creado: el hilo ha sido creado pero aún no ha sido activado. utilización y terminación de procesos y hilos y posteriormente para la sincronización de hilos.

El parámetro IpInfoProceso apunta a una estructura de tipo PROCESS_INFORMATION que CreateProcess habrá rellenado antes de retornar. sea cero. El parámetro IpArgsLineaOr especifica los argumentos de la línea de órdenes que deben pasarse al proceso. no se ejecute aún. lo que hace que se incrementen sus contadores de uso a 2. El parámetro dwIndicadores define los indicadores (flags unidos por el operador OR) que informan al sistema de cómo tiene que ser la creación del nuevo proceso. cada descriptor de un objeto abierto en el proceso que realiza la llamada es heredado por el nuevo proceso. el sistema asigna un identificador único al proceso y otro al hilo primario y antes de que CreateProcess retorne. estos ID son almacenados en los miembros dwProcessId y dwThreadId de la estructura. Siempre que un proceso hijo es autorizado a heredar los descriptores de su padre. El nuevo proceso puede utilizar la función GetCommandLine para obtener la cadena de caracteres correspondiente a los argumentos en la línea de órdenes. Cuando el valor de estos parámetros sea NULL. comunicar a Windows NT el directorio de trabajo para el nuevo proceso. el proceso hijo utilizaría las mismas variables de entorno que su proceso padre. Si el valor de este parámetro es NULL. será el sistema el que se encargue de asignar los descriptores de seguridad por omisión. el sistema crea un hilo primario para este proceso dando lugar a la ejecución de la aplicación el primer argumento especificado en la línea de órdenes. Esto permite a un proceso hilo llamar a CloseHandle para cerrar un descriptor de un objeto abierto (se decrementa su contador de uso) sin afectar a los derechos de su proceso padre.La función CreateProcess crea un espacio de direcciones virtuales de 4Gb para el nuevo proceso y carga la aplicación especificada por 1pNombreAp en dicho espacio de direcciones. Idem para el hilo. para especificar las coordenadas donde la ventana deberá ubicarse. Si el valor de este parámetro es TRUE. el directorio de trabajo es el mismo que el del proceso que llama. Si el valor de este parámetro es NULL. y coloca sus descriptores en los miembros puede liberar el objeto proceso. A continuación. utilizados para especificar como deberá aparecer la ventana principal para el nuevo proceso. El parámetro nDescriptorHeredable indica si el nuevo proceso hereda los descriptores de objetos del proceso que invocó a CreateProcess. si un proceso tiene un descriptor a un objeto de asignación de ficheros heredable y este proceso crea otro proceso. Así mismo. Por ejemplo. Por ejemplo para especificar el nombre de la ventana para una aplicación de consola. El parámetro IpDirectorioActual permite al proceso que invoca a CreateProcess. Si el proceso se crea con éxito. el sistema incrementa el contador de usos de ambos objetos a 1 y a continuación. los cuales podrán ser utilizados por el proceso padre para comunicarse con su hijo. Los descriptores heredados tienen el mismo valor y privilegios de acceso que los descriptores originales. NORMAL_PRIORITY_CLASS indica la clase de prioridad del proceso. El parámetro IpEntorno apunta a un bloque de memoria que contiene las variables de entorno (var=valor) que serán utilizadas por los nuevos procesos. el objeto será destruido. Por ejemplo. etc. etc. CreateProcess abre los objetos proceso e hilo. Durante la creación. Windows NT incremente el contador de usos de los objetos heredados. respectivamente. . la función CreateProcess devuelve TRUE. Sólo cuando ese contador. el sistema asigna al nuevo proceso un descriptor al mismo objeto de asignación de ficheros. el indicador CREATE_SUSPENDED informa al sistema de que el hilo primario del nuevo proceso que se va a crear. El parámetro IpInfoInicial apunta a una estructura de tipo STARTUPINFO que contiene miembros adicionales. cuando un proceso es creado. Los parámetros IpAtributosProcesos y IpAtributosHilo identifican los atributos de seguridad (punteros a estructuras de tipo SECURITY_ATTRIBUTES) que se le asignarán al nuevo proceso y a su hilo primario. el proceso debe terminar ( decrementando el contador de uso a 1) y el proceso padre debe llamar a CloseHandle (decrementando el contador de uso a 0).

este hilo puede invocar a la función CreateThread cuando sea necesario crear otros hilos adicionales para ejecutarse dentro del espacio de direcciones del proceso que invoca a dicha función. La función retorna un descriptor al nuevo hilo. . El objeto proceso pasa al estado libre. DWORD dwTamPila. en el parámetro uCodigoSalida. Si el valor de este parámetro es NULL no puede ser heredado. 4. se asume el tamaño de la pila del hilo primario. Está forma de terminar un proceso no es aconsejable. Creación de un Hilo Hemos visto que cuando invocamos a la función CreateProcess para crear un proceso. Se cierran los descriptores de los objetos abiertos por el proceso. sucede lo siguiente: 1. satisfaciendo a cualquier hilo que estuviera esperando por el proceso para terminar. Todos los hilos del proceso pasan al estado libre. 5. Windows NT no termina un proceso hasta que todos los hilos en ejecución del proceso hayan terminando. El parámetro de dwTamPila específica el tamaño de la pila para el nuevo hilo.Finalizar un Proceso Un proceso termina cuando uno de sus hilos llama a la función ExitProcess VOID ExitProcess( UINT uCodigoSalida // código de salida para todos los hilos ). porque en este caso Windows NT no informa a las DLL (bibliotecas dinámicas) de que el proceso finalizando. Todos los hilos del proceso terminan su ejecución. El proceso padre puede obtener el código de salida de un proceso hijo por medio de la función GetExitCodeProcess y el código de salida de los hilos por medio de GetExitCodeThread. este tamaño será aumentado siempre que sea necesario. 2. Cuando un proceso termina. se genera automáticamente un hilo primario. En cualquier caso. Otra forma de terminar incondicionalmente un proceso es invocar a la función TerminateProcess. si el valor especificado es cero. salvo en casos excepcionales. recibiendo el código de salida para el proceso y pasa sus hilos. LPWORD IpIdHilo ). Su sintaxis es: HANDDLE CreateThread( LPSECURITY_ATTRIBUTES IpAtributosHilo. lo que significa que el proceso no cierra las bibliotecas correctamente. LPVOID IpParam. Está función finaliza un proceso y todos sus hilos. El parámetro IpAtributosHilo es un puntero a una estructura de tipo SECURITY_ATTRIBUTES que determina si el descriptor retornado puede ser heredado por un hilo hijo. El estado de terminación del proceso cambia de STILL_ACTIVE al código de salida de proceso. satisfaciendo a cualquier hilo que estuviera esperando por otro hilo para terminar. A su vez. LPTHREAD_START_ROUTINE IpDirreccionInicio. 3.

El contador de uso del objeto hilo no se incrementa y cualquier llamada a CloseHandle no tiene efecto. Cualquier otro hilo puede preguntar por este código invocando a la función GetExitCodeThread. El parámetro IpIdHilo apunta una variable de 32 bits que recibe el identificador que Windows NT asigna al nuevo hilo. El valor de este parámetro no puede ser NULL. si el valor es cero. Para conocerlo debe invocar a la función: HANDLE GetCurrentThread(VOID). porque en realidad lo que se obtiene es un seudodescriptor. El parámetro dwIndicadores define los indicadores (flags) que informan al sistema de cómo tiene que ser la creación del nuevo hilo. El descriptor obtenido por las funciones GetCurrentThread y GetCurrentProcess sólo puede utilizarse en el contexto del hilo actual.LPDWORD IpCodigoSalida). Por ejemplo. su descriptor es retornado al hilo que invocó a CreateThread pero el nuevo hilo desconoce cuál es su propio descriptor. En este caso decimos que el hilo se suicida. También se puede terminar un hilo invocando a la función TerminateThread: BOOL TerminateThread (HANDLE hHilo. Este dato inicial puede ser un valor de 32 bits o un puntero a una estructura que contenga información adicional. La función acepta un único parámetro de 32 bits y devuelve un valor de 32 bits. el nuevo hilo empieza a ejecutarse inmediatamente. puede obtener dicho descriptor invocando a la función: HANDLE GetCurrentProcessID(VOID). recibiendo el código de salida en el parámetro dwCodigoSalida. El parámetro IpParamHilo es el argumento pasado a la función fnHilo cuando se comienza la ejecución del hilo. Cuando un hilo invoque a una función que requiere el descriptor del proceso. cuando un hilo invoque a una función que requiera el identificador del proceso. puede obtener dicho descriptor invocando a la función: HANDLE GetCurrentProcessID(VOID). pero si el valor es CREATE_SUSPENDED la ejecución del nuevo hilo queda suspendida. Cuando necesitemos un descriptor real utilizaremos la función DuplicateHandle. El valor retomado por la función es el código de salida del hilo. Cuando se crea un hilo. Si el hilo no ha terminado. Finalizar un Hilo Un hilo termina de forma natural cuando retorna de su función. ExitThread finaliza un hilo.El parámetro IpDireccionInicio indica la dirección de la función donde comenzará la ejecución del nuevo hilo.DWORD dwCodigoSalida ). BOOL GetExitCodeThread( HANDLE Hhilo. DWORD fnHilo ( LPVOID IpParamHilo). Otra forma de de terminar un hilo es invocando a la función ExitThread: VOID ExitThread (DWORD dwCodigoSalida). Así mismo. . Esto es así. IpCodigoSalida vale STILL_ACTIVE.

Así. Se cierran todos los descriptores de los objetos que son propiedad del hilo. cuando todos los hilos de prioridad 31estén durmiendo. Para enviar un hilo al estado dormido hay que invocar a la función SuspendThread: DWORD SuspendThread (HANDLE hHilo). 3. por ejemplo. La mayoría de los hilos consumen su tiempo durmiendo. un poco inferior. recibiendo el código de salida en el parámetro de dwCodigoSalida. cuando el hilo no responda. lo que permite la ejecución de los hilos de prioridades bajas con una frecuencia.En este caso decimos que el hiso es asesinado. los hilos tiene asignados prioridades de1 a 31 (31 es la prioridad más alta) de forma que cuando el sistema operativo asigna un intervalo de tiempo de UCP a un hilo. sucede lo siguiente: 1. trata de igual forma a UCP al primer hilo activo de prioridad 31. El objeto hilo pasa al estado libre. Cuando un hilo termina. Windows NT asigna intervalos de tiempo a cada uno de los hilos (si la máquina tuviera varias UCP. Se puede enviar un hilo al estado dormido varias veces. se empieza otra vez por el primero. Cuando todos los hilos de prioridad 31 han tenido su intervalo de tiempo. 2. La asignación de los intervalos de tiempo se realiza en función de la prioridad que tenga asignada el hilo activo. TerminateThread finaliza el hilo especificado. El proceso termina si el hilo es el último hilo activado del mismo. Pero. probablemente. satisfaciendo a cualquier hilo que estuviera esperando por él para terminar. pero no puede autoactivarse. Un hilo dormido puede ser activado invocando a la función ResumeThread: DWORD ResumeThread (HANDLE hHilo). porque en este caso Windows NT no informa a las DLL (bibliotecas dinámicas) de que el hilo está finalizando. Un razonamiento análogo nos conduce a pensar que los hilos de prioridad 29 sólo pueden ejecutarse cuando los hilos de prioridades 31 y 30 estén durmiendo. lo que significa que el hilo cierra las bibliotecas correctamente. Esta forma de terminar un hilo no es aconsejable. lo que implica activarle otras tantas veces. devuelven las veces que el hilo fue enviado previamente al estado dormido. . En Windows NT. Planificación de Hilos Cuando se ejecuta un proceso que tiene varios hilos activos. Parece entonces. asigna tiempo al siguiente hilo de prioridad 31 y así sucesivamente. Un hilo puede enviar a dormir a otro hilo o así mismo. que los procesos de prioridad 1 nunca se ejecutarán o que se ejecutaran de tarde en tarde. 4. el sistema asigna tiempos a los hilos activos de prioridad 30. la verdad es que no es así. cundo este finaliza su intervalo de tiempo. Ambas funciones ResumeThread y SuspendThread . El estado de terminación del hilo cambia de STILL_ACTIVE al código de salida del hilo. salvo en casos excepcionales. por motivos diferentes. entonces Windows NT asignaría una a cada hilo que estuviera esperando por ella). antes de que sea puesto en ejecución. Según lo expuesto ¿cómo permitir la ejecución de hilos con prioridad inferior? La respuesta está en saber que muchos hilos del sistema son suspendidos de vez en cuando.