|
|
Tema 7. Señales y Funciones de Tiempo. |
1. CONCEPTO DE SEÑALLas señales son interrupciones que pueden ser enviadas a un proceso para informale de algun evento asíncrono o situación especial, se emplea tambien para referirse al evento. Los procesos pueden enviarse señales unos a otros a través de la llamada kill. Cuando un proceso recibe una señal puede reaccionar de tres formas distintas:
Cada señal tiene asociado un numero entero positivo y, cuando un proceso le envia una señal a otro le envia este numero. En el UNIX System V hay definidas 19 señales, y estas señales las tienen prácticamente todas las versiones de UNIX, y luego cada fabricante le añade las que considera necesarias. Las señales se clasifican en los siguientes grupos:
Esas señales son las siguientes: 3. SEÑALES EN EL UNIX SYSTEM VUna señal es un evento que debe ser procesado y que puede interrumpir el flujo normal de un programa. Una señal puede asociarse con una función que procesa el evento que ha ocurrido, el evento no interrumpe el fuljo del programa. ENVÍO DE SEÑALES (KILL Y RAISE)La subrutina Kill envía una señal a uno o varios procesos desde otro proceso. Su formato es: #include <signal.h>int kill (pid_t pid,int sig) Pid que es el identificador de los procesos que recibirán la señal. Los valores que puede tomar son:
La acción por defecto para la señal puede:
EJEMPLO DEL USO DE KILL#include < signal.h>main() { int pid; if (( pid = fork()) == 0) { while(1) { printf("HIJO.PID = %d\n", pid); sleep(1); } } sleep(10); printf("PADRE. Terminación del proceso %d\n", pid); kill (pid,SIGTERM); exit(0); } Este ejemplo crea un hijo y mientras exista debe mostrar la palabra hijo cada segundo y el padre a los 10 segundos debe mostrar la palabra PADRE y terminar el proceso del hijo con la señal SIGTERM. La subrutina raise envía señales al proceso actual, es decir un proceso así mismo. Su formato es: #include < signal.h>int raise(int sig); Los parámetros son: SIG es el numero d las señal que queremos enviar; RAISE se codifica con kill de esta forma:< int raise(int sig){ return kill(getpid(),sig); } TRATAMIENTO DE SEÑALES (signal)La subrutina signal asocia una acción determinada con una señal, es del tipo función que devuelve un puntero a una función void y recibe dos parámetros : Su formato es: #include < signal.h>void (*signal 8int sig, void(*action) ())) (); Los parámetros son: -SIG numero de señal-ACTION Puntero a la rutina asociada con la señal o uno de los valores siguientes: SIG_DFL : acción por defecto para dicha señal, consiste en terminar el proceso y a veces generar un fichero core. SIG_IGN ignorar la señal. DIRECCION, es la dirección de la rutina de tratamiento de la señal. Su declaración debe ser de esta forma: #include < signal.h>void handler (int sig [, int code, struct sigcontext *scp]); La rutina handler es la rutina a la cual el núcleo llama y la pasa los parámetros sig,code y scp, su llamada es asíncrona, es decir, se puede dar en cualquier momento de la ejecución del programa y debe estar codificada para tratar las situaciones en las que se produce el envío de señales. Sus parámetros son: sig, numero de las señalcode, palabra que contiene información del estado del hardware en el momento de invocar a handler (parámetro opcional). scp contiene información de contexto definida en < signal.h>. (parámetro opcional). La llamada a signal devuelve el valor que tenia la action, que sirve para restaurarlo, si se produce un error signal devuelve SIG_ERR y en errno estará el código del error. Los valores SIG_DFL, SIG_IGN Y SIG_ERR son direcciones de funciones para que puedan devolver signal y deben estar siempre vacías. Se soluciona definiéndolas de esta forma: #define SIG_DFL ((void(*) ())0)#define SIG_IGN ((void(*) ())1) #define SIG_ERR((void (*) ())-1) La conversión de las constantes hace que estas sean tratadas como direcciones de inicio de funciones que no contienen ninguna función porque en todas las arquitecturas UNIX son zonas reservadas para el núcleo. EJEMPLO USO DE SIGNAL(signal-1.c)#include < stdio.h>#include < signal.h> /*** main:inicializa el manejador de la señal SIGINT y se pone en espera para recibir la señal. ***/ main() { void manejador_SIGINT(); if (signal(SIGINT, manejador_SIGINT) == SIG_ERR) { perror ("signal"); exit(-1); } while(1) { printf ("En espera de Ctrl-C\n"); sleep (999); } } /*** manejador_SIGINT : rutina de tratamiento de la señal SIGINT. ***/ void manejador_SIGINT (int sig) { printf("Señal número %d recibida.\n", sig); } El ejemplo anterior hace que la primera vez que se pulse Ctrl-C aparezca un mensaje que ponga En espera de Ctrl-C y que espere 999 segundos pero la segunda vez que se pulsa Ctrl-C hace que se muestre el numero de la señal y que ha sido recibida y termina la ejecución del proceso, esto es porque el nucleo llama a la rutina de tratamiento, se restaura la rutina por defecto, que se encarga de terminar el proceso. Para resolver el problema se hace este tratamiento: void manejador_SIGINT (int sig){ static cnt = 0; printf("señal número %d recibida.\n",sig); if (cnt < 20) printf ("Contador = %d\n", cnt ++); else exit(0); if (signal (SIGINT, manejador_SIGINT) == SIG_ERR) { perror ("signal"); exit(-1); } } Esto hace que la rutina de tratamiento siga siendo la misma y se terminara a los 20 segundos. Si recibimos una señal mientras que tratamos otra del mismo tipo con el caso anterior se terminaría el proceso ya que al recibirse la señal por primera vez la nueva rutina de tratamiento pasa a ser la rutina por defecto. Para bloquear la recepción de señales de un tipo mientras tratamos otra haremos una llamada a signal pasándole el parámetro SIG_IGN. Como en el siguiente ejemplo: void manejador_SIGINT (int sig){ static cnt =0; if (signal (signal (SIGINT, SIGIGN) == SIG_ERR){ perror ("manejador_SIGINT"); exit(-1); } printf ("Señal número %d recibida.\n",sig); if (cnt< 20) printf("Contador = %d\n", cnt++); else exit(0); signal(SIGINT, manejador_SIGINT); } Otra opción es que una señal interrumpa a la rutina de tratamiento sin que haga terminar el proceso, pero no es aconsejable ya que puede que la fuente de señales las genere a una velocidad mayor que la de tratamiento del manejador y esto haría que se desbordara el programa. Por ejemplo: void manejador_SIGINT(int sig){ static cnt = 0; signal(SIGINT,manejador_SIGINT); printf("Señal número %d recibida.\n", sig); if (cnt < 20) print ("Contador = %d\n", cnt++); else exit(0); } Bajate esta documentación en un archivo Acrobat Reader |