Señales (signals)

Introducción
    Las señales son interrupciones de software.
   Permiten el manejo de eventos asíncronos.
Conceptos
    Cada señal tiene un nombre. Ellos comienzan con SIG. Ej. SIGABRT
    El archivo < signum.h > contiene la definición de todas las señales. La ubicación definitiva de este archivo depende del sistema operativo de su máquina. Generalmente se puede llegar a él vía /usr/include/signal.h. En Aragorn este archivo está en: /usr/include/bits/signum.h
    Numerosas condiciones pueden generar señales. Entre ellas:

    Hay tres cosas que un programa puede solicitar al kernel como acción cuando llegue una señal:
    1.     Ignorar la señal. Sin embargo hay dos señales que no pueden ser ignoradas: SIGKILL y SIGSTOP.
    2.     Capturar la señal. El kernel llama una función dentro del programa cuando la señal ocurre.
    3.     Permitir que se aplique la acción por defecto. Esta puede ser: terminal el proceso, terminar con un core dump, parar el proceso, o ignorar la señal.
      Algunas señales:
    SIGALRM: generada cuando el timer asociado a la función alarm expira. También cuando el timer de intervalo es configurado (setitimer)
    SIGCHLD: Cuando un proceso termina o para, el proceso envía esta señal a su padre. Por defecto esta señal es ignorada. Normalmente el proceso padre invoca la función wait para obtener el estatus de término del proceso hijo. Se evita así la creación de procesos "zombies".
    SIGCONT: es enviada para reanudar un proceso que ha sido parado (suspendido) con SIGSTOP.
    SIGINT: generada con DELETE o Control-C
    SIGKILL: Permite terminar un proceso.
    SIGTSTP: generada cuando presionamos Control-Z. Puede ser ignorada.
    SIGSTOP: similar a SIGTSTP pero no puede ser ignorada o capturada.
    SIGUSR1: Es una señal definida por el usuario para ser usada en programas de aplicación.
    SIGUSR2: Otra como la anterior.
    Para probar las señales se recomienda usar
% kill -SIG??? <pid>   
No use los números de determinado sistema, pues éstos han cambiado en el tiempo.
Función signal

    #include <signal.h>
    void (*signal (int signo, void (*func) (int)))(int);

Este prototipo es equivalente a:
typedef    void Sigfunc(int);
Sigfunc * signal (int signo, Sigfunc *);

signo: es el nombre de la señal.
func: puede ser la constante SIG_IGN para ignorar la señal, SIG_DFL para usar la acción por defecto, o puede ser una función definida por el programa. Esta función es conocida como el manejador de la señal (signal handler). El valor retornado es un puntero al manejador previamente instalado para esa señal.

Ejemplo: uso de SIGUSR1 y SIGUSR2

Interrupción de llamados al sistema
    Cuando llega una señal ésta es atendida por su manejador. Luego es el manejador se debe poner cuidado de no invocar funciones no re-rentrantes. Éstas son las que no permiten su re-invocación antes de su término; por ejemplo strtok.

Cuando llega una señal a proceso bloqueado, por omisión este sigue bloqueado luego de atendida la señal. En ocasiones deseamos que éste salga del bloqueo. Por ejemplo para suspender una entrada de datos cuando ésta se demora mucho. Hay formas de obtener este comportamiento ver ejemplo readInterrupted.c.

Para ver comportamiento actual ver tarea "rapidez aritmética" y las soluciones de Daniel Durán y Gonzalo Vásquez

Funciones kill y raise
    kill envía una señal a un proceso o a un grupo de procesos. raise permite enviar señales al mismo proceso (hacia uno mismo).
    #include <sys/types.h>
    #include <signal.h>
    int kill (pid_t pid, int signo);

    int raise(int signo);

    Este último es como kill( getpid(), signo);

Funciones alarm y pause
    La función alarm permite especificar un timer que expire en un tiempo dado.
    La función pause suspende al proceso llamador hasta que llegue una señal.

    #include <unistd.h>
    unsigned int alarm(unsigned int seconds);

    En versiones actuales de sistema operativo también se puede usar
    unsigned int ualarm(unsigned int microseconds);

    int pause(void); /* retorna -1 con errno en EINTR */

    El control del tiempo no es exacto por la incertidumbre del kernel, carga del sistema, etc.
    Hay sólo una alarma por proceso!
    La alarma se puede cancelar con seconds=0 en el argumento. El valor retornado es lo que falta para que el reloj expire.

    Ejemplo 1 de uso de alarma: sleep() vía programa .
    Explicar por qué esta función tiene los siguientes problemas: ¿Qué pasa si el llamador ya tiene la alarma configurada?. ¿Que pasa si el programa tenía otro manejador definido previamente? ¿hay alguna carrera crítica?

    Ejemplo 2 de uso de alarma, llamado a read con time out: Versión de prueba para sisemas con read interrumpible, Versión para sistemas con read no interrumplible.

Función sleep
    sleep suspende al proceso llamador por la una cantidad de segundos indicada o hasta que se reciba una señal.
    #include <unistd.h>
    unsiged int sleep (unsigned int seconds);

    Hoy también tenemos disponible
    unsiged int usleep(unsigned int microseconds);

    Retorna 0 ó el tiempo que no se durmió.      

    También se dispone hoy de:
    #include <time.h>
       int nanosleep(const struct timespec *req, struct timespec *rem);