Top Banner

Click here to load reader

Creacion de Hilos en Windows y Linux_vasquezcampos_ciertocordova

Jan 02, 2016

Download

Documents

Sheyla Vasquez
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript

Creacin de Hilos en Windows y Linux

2

AO DE LA INVERSIN PARA EL DESARROLLO RURAL Y LA SEGURIDAD ALIMENTARIAUNIVERSIDAD NACIONAL DEL SANTA

FACULTAD DE INGENIERIAESCUELA DE INGENIERA DE SISTEMAS E INFORMTICA Tema: Creacion de Hilos en Windows y Linux Docente: Carlos Gil NarvaezAsignatura: Topicos IICiclo: VIIIIntegrantes: Cierto Crdova Juan Vsquez Campos SheylaFecha de Entrega:24/10/2013 Ao: 2013

CREACION DE HILOS EN WINDOWSI. IntroduccinEs habitual que los programadores Windows desarrollen sus programas sin conocer realmente cmo funciona el sistema de mensajera de Windows. Aunque esto ltimo no es un requisito indispensable para programar, su desconocimiento puede llevar a programas ineficientes o con errores.Antes de analizar cmo funciona el mismo, es necesario presentar un nuevo concepto, las threads.II. ThreadsUna thread es bsicamente un hilo o camino de ejecucin. Es la unidad mnima de ejecucin que Windows puede manejar, est asociada al proceso que la cre, y est identificada por un nmero entero de 32 bits.Una thread est compuesta por: Un nmero entero de 32 bits que la identifica unvocamente El estado de los registros del procesador Una pila (stack) Parmetros de seguridad (Session ID y Security Descriptor) Un valor de prioridad. La prioridad real de la thread es funcin de este valor y la prioridad del proceso a que pertenece. Una entrada en la lista de ejecuciones de Windows Opcionalmente, una cola de mensajesCada thread tiene asociado un usuario (SID: Session ID) y unos parmetros de seguridad (SD Security Descriptor), que determinan los permisos de la thread dentro del sistema operativo, y que por defecto coinciden con los de la thread principal del proceso.Un proceso se compone se una o ms threads, adems del cdigo, los datos y otros recursos del programa. Todas las threads de un proceso comparten tanto el cdigo como los datos y se ejecutan en forma independiente. Es por ello que, cuando una o ms threads deban trabajar con un mismo conjunto de datos, deber utilizarse un sistema de semforos, para evitar que los datos puedan verse corrompidos.La creacin de una thread se realiza utilizando la funcin CreateThread(). Veamos un ejemplo:

Al llamar el programa a la funcin Funcion(), el programa crea una thread, llamando a la funcin CreateThread(). Esta nueva thread comenzar su ejecucin en la funcin ThreadProc(), y terminar cuando la misma retorne de esta funcin, o llame a la funcin ExitThread(). No existe ninguna forma segura de terminar una thread desde otra thread, y si bien existe la funcin TerminateThread() para tal fin, no se recomienda su uso.Si se est utilizando MFC, no debe utilizarse la funcin CreateThread(), sino AfxBeginThread(), cuya sintaxis es similar. Esto debe hacerse, porque las MFC implementan internamente una lista de las threads que el programa est utilizando, y redefinen la funcin de creacin de threads para poder conocer esto. El no utilizar esta funcin puede llevar a prdida de memoria o a un programa inestable, si dentro de la thread se utilizan funciones de las MFC.La funcin CreateThread() devuelve dos valores. El identificador de la thread o dwThreadId, que identifica a la thread, y un handle a la thread hThread, que permite realizar operaciones sobre la misma. Se podra preguntar porqu Windows utiliza un handle para realizar operaciones sobre la thread, y no directamente el Thread ID. La respuesta es que el handle a la thread no slo identifica una thread, sino que define las operaciones que estn permitidas realizar sobre la misma.El handle devuelto debe ser cerrado cuando ya no se use, utilizando CloseHandle(), y puede ser utilizado entre otras cosas para conocer el estado de la thread.III. C Runime Library y MultithreadingMuchas funciones de C no fueron pensadas para trabajar con multithreading. Por ejemplo, la funcin time() clsica devuelve a un puntero a una estructura esttica time_t, que si fuese llamada en paralelo por varias threads de un proceso podra ser sobreescrita. Para solucionar esto, es necesario que esta librera de C en tiempo real (C run-time library o C RTL) realice algunas operaciones adicionales al crearse o liberarse una thread. Por este motivo la C-RTL proporciona sus propias funciones de creacin / terminacin de threads: beginthread() / exitthread().Las MFC proporcionan tambin sus propias funciones de creacin / terminacin de threads: AfxBeginThread() y AfxExitThread().De ms est decir que la llamada a funciones de threads del API es ms eficiente que sus pares C, y estas ms eficientes que las de las MFC, ya que las ltimas deben llamar a su vez a las anteriores.Cualquiera de los tres grupos de funciones de threads puede utilizarse, siempre y cuando se respete la siguiente tabla.

OperacinFunciones APIEj:CreateThread()Funciones de la librera de C. Ej:Beginthread()MFCEj:Afxbeginthread()

Llamada a funciones APISiSiSi

Llamada a funciones CNoSiSi

Uso de funciones/clases MFCNoNoSi

IV. MensajesAntes de ver como implementa Windows el sistema de mensajera, repasemos algunos conceptos fundamentales: Un programa o aplicacin es visto por el sistema operativo como un proceso, identificado por un nmero entero, llamado process ID o PID. Un proceso se compone de una o ms threads, cada una de las cuales est identificada por un nmero entero llamado Thread ID. Cada thread puede o no tener asociada una cola de mensajes. Toda ventana tiene asociada una funcin (callback) de procesamiento de mensajes. Toda ventana tiene asociada un proceso y una thread, que es la thread que la cre. La funcin callback de la ventana es ejecutada siempre por la thread que cre la ventana. Si bien no se lo explic, se dijo que todo programa (o en realidad thread) que procese mensajes tendr un cdigo similar al siguiente:MSG Msg;while (GetMessage (&Msg, NULL, 0, 0);{TranslateMessage (&Msg);DispatchMessage (&Msg);}Cuando un programa encola/enva un mensaje a una ventana, este mensaje es en realidad encolado/enviado a la thread de la ventana. Por eso, para que una thread pueda recibir mensajes, previamente debe crear la cola de mensajes, lo cual ocurre automticamente cuando la misma llama por primera vez a cualquiera de las funciones de lectura de mensajes (GetMessage() en nuestro caso).El envo de mensajes (SendMessage ()) y el encolado de mensajes (PostMessage()) es procesado de forma diferente, razn por la cual veremos estos casos por separado.V. Encolado de mensajesEl manejo de mensajes se muestra esquemticamente a continuacin:

1. Cuando un programa encola un mensaje para una ventana utilizando la funcin PostMessage(), el mismo es insertado dentro de la cola de mensajes de la thread de la ventana, y en el campo hWnd del mensaje indica la ventana destino. Funcin PostMessage() retorna inmediatamente y la thread que encol el mensaje contina su procesamiento normal.Existe tambin la funcin PostThreadMessage(), que trabaja en forma similar a PostMessage(), slo que en lugar de indicarse un ventana como destino, se indica una thread. En este caso, el mensaje ser encolado en forma idntica, pero el campo hWnd tendr un valor NULL.2. La funcin GetMessage() / PeekMessage() lee y/o retira un mensaje de la cola. Los mensajes encolados por la propia thread tienen prioridad por los encolados por cualquier otra thread.3. La funcin TranslateMessage(), realiza ciertas conversiones, como por ejemplo, modificar un click en el botn en un mensaje WM_CLOSE.4. La funcin DispatchMessage() llama a la funcin callback de la ventana correspondiente, la cual procesa el mensaje y devuelve una respuesta.5. Si la funcin que envi el mensaje fue PostMessage() / PostThreadMessage(), esta respuesta se descarta.VI. Envi de mensajesSi una misma thread enva un mensaje (SendMessage()) para la misma thread, este mensaje es no es encolado sino que es enviado directamente a la funcin callback, como una llamada a una funcin cualquiera.Si una thread cualquiera enva un mensaje a una ventana de otra thread, no es posible llamar directamente a la funcin callback de otra ventana. Si esto se hiciese as, el cdigo de la funcin callback se ejecutara bajo los parmetros de seguridad de la thread que envi el mensaje, lo cual vulnerara toda la seguridad del sistema. Por ejemplo, si Windows simplemente llamase a la funcin callback para informar un evento como el movimiento del mouse, esta funcin se ejecutara con los parmetros de seguridad del kernel, lo cual permitira la aplicacin realizar cualquier operacin.Para evitar esto, cuando una thread enva un mensaje para una ventana de otra thread, este mensaje es encolado en forma similar a un PostMessage(), pero la thread permanece bloqueada, hasta que la thread destino procese el mensaje y devuelva su respuesta.VII. Problemas que pueden presentarseEl sistema de procesamiento de mensajes de Windows no es infalible, si bien generalmente se programa como si lo fuese. Entre existen dos problemas comnmente ignorados que deben ser tenidos en cuenta:

Cuando una thread enva un mensaje para otra thread, la primera permanece en estado de espera hasta que la segunda procese el mensaje y devuelva una respuesta. Si la segunda thread se colg o por cualquier razn no procesa mensajes, la segunda quedar esperando indefinidamente. Para evitar esto, es posible utilizar la funcin SendMessageTimeout(), que permite especificar el tiempo que debe esperarse la respuesta. Transcurrido este tiempo, la funcin retorna especificando un cdigo de error. Es comn que los programadores crean que Windows garantiza el envo de los mensajes. Sin embargo esto no es as. La cola de mensajes de Windows, (como toda cola) es finita, y por ende es posible que se llene. Las funciones PostMessage() y PostThreadMessage() retornan siempre un valor indicando si el mensaje pudo o no ser encolado.Un programa en WindowsEn el nivel ms bsico, un programa en Windows inicializa algunas cosas y entra en algo llamado un "Ciclo de Eventos" (Event Loop). En este ciclo el programa sigue recibiendo mensajes de Windows hasta que encuentre un mensaje llamado WM_QUIT o WM_CLOSE.Utilicemos un poco de "pseudocdigo" para ver a qu nos referimos: Programa MyPrograma;

Comenzar Inicializa; CreaFormasYVentanas; MensajeWindows = HayNuevosMensajes; mientras ( MensajeWindows WM_CLOSE o MensajeWindows WM_QUIT ) comenzar ProcesaMensaje(MensajeWindows); terminar; CierraFormasyVentanas; Finaliza; Terminar.

procedimiento ProcesaMensaje( MensajeWindows ); comenzar en caso de MensajeWindows = WM_RESIZE : CambiaTamao; MensajeWindows = WM_CLICK : ProcesaClicks; MensajeWindows = WM_PAINT : RedibujarPantallas; { Aqu listamos todos los posibles mensajes que debemos procesar } fin caso; terminar;

Como ver usted, desde el punto de vista del procedimiento el programa no es ms que un programa que entra en un ciclo infinito. As que, aun cuando "se siente" que el programa puede hacer varias cosas al mismo tiempo, en realidad cada proceso de un mensaje de Windows prohbe el proceso de otros mensajes hasta que su programa termine.Este es el motivo por el cual, cuando usted entra en un ciclo infinito, su programa "se atora". Se atora porque no puede procesar mensajes hasta que usted termine su ciclo, y esperar pacientemente a que su ciclo termine. Cuando su programa se atora, si usted pasa una ventana sobre las ventanas del programa, el programa "se borra". Esto es porque uno de los mensajes de Windows que su programa procesa es el mensaje WM_PAINT. Como el mensaje no puede ser procesado, las porciones de la ventana que estn en blanco no son "redibujadas". El usuario tampoco puede mover las ventanas o minimizarlas, porque estos tambin son mensajes que hay que procesar.

CREACIN DE HILOS EN LINUXI. Procesos Linux - exec y fork Un proceso es un programa en ejecucin. El proceso est formado por el cdigo del programa y el conjunto de datos asociados a la ejecucin del programa. El proceso adems posee una imagen de memoria, esto es el espacio de memoria en el que est autorizado. La imagen de memoria puede estar referida en memoria virtual o memoria fsica. Adems en cuanto al ciclo de vida hablar acerca del que se tiene en cuenta en la planificacin a corto plazo (ciclo de vida simple). Esta planificacin es en la que se decide el siguiente proceso a ejecutar (FIFO, Round Robin, SJF). Este ciclo de vida posee 4 estados: Listo para ejecutarse, en ejecucin, bloqueado y fin.

Podemos tener en ejecucin tantos procesos como procesadores tenga nuestro equipo. El fin de tiempo en ejecucin lo decide el algoritmo de planificacin que utilice nuestro sistema operativo. Esta expulsin del procesador provoca un cambio de contexto.

Unavez visto esto comprendemos algo mejor el funcionamiento de los procesos en nuestro sistema. Aunque esta sea una imagen demasiado general y alejada siempre es conveniente tener una mnima idea acerca de esto (el tema del cambio de contexto es importante en la concurrencia). En esta entrada estamos hablando de procesos pesados (programa con un solo hilo de ejecucin), pues en procesos ligeros la cosa cambia.Los nuevos procesos obtienen los recursos directamente del sistema operativo o el proceso padre debe compartir recursos. Acerca de los nuevos procesos en Linux, debemos diferenciar entre: crear un nuevo proceso y ejecutar nuevos programas.

La llamada al sistema que empleamos para crear un nuevo proceso se denomina fork(). La llamada fork() crea una copia casi idntica del proceso padre (se copia todo el cdigo) y continan ejecutndose en paralelo. El proceso padre recibe de fork() el id del hijo, mientras que el hijo recibe un 0. Elhijo adems hereda recursos del padre (ficheros, abiertos, estado de las variables, etc...), sin embargo hay otros recursos que no se heredan como por ejemplo las seales pendientes, devuelve -1 en caso de error. La funcin est declarada tal que as: size_t fork(void);

Por otra parte tenemos la funcin exec(). Esta funcin cambia la imagen del proceso actual. Lo que realiza es sustituir la imagen de memoria del programa por la de un programa diferente. Esta funcin normalmente la invocaremos en un proceso hijo previamente generado por fork(). Existen diferentes funciones para exec, sus declaraciones son: int execl(const char *path, const char *arg, ...); int execv(const char* path, char* const argv[]); int execve(const char* path, char* const argv[], char* const envp[]); int execvp(const char *file, char *const argv[]);En path debemos pasar la ruta del ejecutable, file: Busca el archivo ejecutable en todos los directorios especificados por PATH. Esta funcin retorna -1 en caso de error, en caso contrario no retorna. No retorna debido a que hemos sustituido la imagen del programa actual por la de un nuevo programa. Debemos pasar los argumentos para el nuevo programa a ejecutarse en *arg. Adems se heredan los descriptores de ficheros abiertos y todas las seales pasaran a la accin por defecto.

Cabe mencionar que lo que se debe hacer es dedicar al padre a crear hijos y estos que realizan trabajo por l. Adems el padre puede crear ms hijos o esperar a que termine a que termine el hijo. Esta esperar se realiza con la funcin wait(). Esta funciona pasa al padre al estado bloqueado hasta que acabe el hijo. Recomiendo que en nuestro terminal realicemos "man" de todas las funciones para comprenderlas mejor si an no lo entendemos.

El modelo de la funcin fork() posee ciertas ineficiencias: Se copia una gran cantidad de datos que podran compartirse. Adems si lo empleamos para cargar otra imagen es todava peor. Todo se desecha.En muchos sistemas de UNIX se mejora usando COW (Copy-On-Write). Esto nos ayuda de la siguiente forma: Retrasa la copia de datos. Se copian los datos si se intentan modificar. Se copia la tabla de pginas del padre (no su contenido).Por ltimo expondr un ejemplo simple de estas funciones:

Este ejemplo es bastante sencillo. El proceso padre genera un hijo y espera a que termine. El proceso hijo crea una nueva imagen de programa llamando al comando "ls -l". El switch nos sirve para saber dnde nos encontramos. Hay que recordar que el hijo en id tiene 0 y el padre el id del hijo.

II. Linux - Threads

Ya se ha explicado la manera de crear diferentes procesos en Linux. En estos procesos tan solo tenamos una ejecucin secuencial del cdigo. Sin embargo, la unidad mnima de procesamiento es el hilo. Es decir, podemos tener un proceso que ejecuta diferentes hilos de ejecucin. Adems estas aplicaciones multihilos tienen un menor consumo que las aplicaciones multiproceso. Un hilo es mucho ms rpido de crear que un proceso. Al cambiar de un hilo a otro se produce un cambio de contexto. Cada hilo posee: Identificador Pila Conjunto de registros Contador de programaLos hilos comparten ciertos recursos con el resto de hilos como son: Seales Mapa de memoria Ficheros abiertos Semforos TemporizadoresLos problemas que pueden presentar los hilos, como ya he dicho, los contemplar en entradas posteriores. Por tanto a partir de ahora hablar acerca de cmo funcionan en C. Las funciones necesarias para el manejo de hilos en C se encuentran en la biblioteca pthread.h. Estas funciones son:1. int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*rutina)(void *), void *arg)2. pthread_t pthread_self(void)3. int pthread_join(pthread_t thread, void **value)4. int pthread_exit(void *value)En pthread_create tenemos 4 parametros. El primer parmetro guardar el identificador del thread que creamos. El segundo parmetro es un puntero a una struct con los atributos del hilo (podemos pasar NULL). Estos atributos los comentar en otra entrada. El tercer parmetro es un tipo de puntero especial, este puntero es un puntero que recibir una funcin. Esta funcin ser lo que ejecute nuestro hilo. Debemos tener en cuenta que se pasa la direccin de esta funcin no los parmetros, para esto tenemos el ltimo argumento que es un puntero y tan solo podemos pasar un parmetro. Como tan solo se puede pasar un parmetro se recomienda que sea una struct que contenga todos los datos que deseamos.

La funcin pthread_t pthread_self devuelve el identificador de hilo del hilo que la ejecuta. Por otra parte tenemos la funcin pthread_join esta funcin servir para esperar a otro hilo. Recibe un identificador de hilo (parmetro thread) al que debemos esperar. El segundo parmetro es el valor de terminacin del hilo. La ltima funcin es pthread_exit y es la que finaliza el hilo. Recibe un parmetro que ser el valor de terminacin (en forma de puntero).

Tres de estas funciones devuelven un int, es decir en caso de error devuelve -1. Todo lo anteriormente mencionado se puede mostrar con un ejemplo bastante sencillo:

Veamos una de las posibles ejecuciones de este programa:

Bibliografa

http://www.hackerdude.com/courses/delphi/Cap011.1.htmlhttp://cortesfernando.blogspot.com/2011/11/procesos-linux-exec-y-fork.htmlhttp://cortesfernando.blogspot.com/2011/11/linux-threads.htmlhttp://www.uhu.es/17222-18221/WEB_ANTIGUA/creahilos.pdf

2