Señales

Las señales son un mecanismo para comunicar eventos a los procesos cuando un proceso recibe una señal, la procesa inmediatamente. Cuando un proceso recibe una señal puede: Ignorar a la señal, cuando es inmune a la misma, Invocar la rutina de tratamiento por defecto, Invocar a una rutina de tratamiento propia.

 Durante una sesión cualquiera, el número de procesos depende del trabajo que los usuarios realicen. Se sabe que los procesos tienen su propio contexto, pero esto no quiere decir que estén incomunicados entre sí. Existe un conjunto de métodos mantenidos por el kernel que permiten entablar diálogos entre ellos. Estos métodos se llaman mecanismos IPC (Interprocess Comunication). Dentro del conjunto de IPC’s se tienen a los semáforos, la memoria compartida, colas de mensajes, etc. Estas no son las únicas formas de intercomunicación de que dispone el sistema operativo Unix. Los procesos también pueden enviarse interrupciones software, señales. El conjunto de señales lo maneja el gestor de señales. El número y tipo de señales viene impuesto por el sistema operativo y cada una de ellas será empleada en un caso concreto siendo su número la única información que realmente se transmite entre los procesos cuyo significado dependerá de la interpretación del programador.

Como indica la palabra interrupción, este tipo de llamadas son producidas por el kernel o por otro proceso de forma inesperada y tiene como finalidad parar o desviar el curso normal de las instrucciones que se ejecutan. Una señal puede ser recibida por un proceso si este incurre en un error en coma flotante, si se produce un error de acceso a memoria, si se intenta acceder a una dirección de memoria fuera de su segmento de datos, etc.

Como anteriormente se dijo, el número y significado de las señales depende del tipo de sistema operativo Unix que se tenga instalado. En el fichero de cabecera signal.h están definidas todas las señales, número y nombre.

Comportamiento:

Cuando un proceso recibe una señal, puede tratarla de tres formas diferentes:

1.-        Ignorar la señal, con lo cual no tiene efecto.

2.-        Invocar a la rutina de tratamiento correspondiente al número de señal. Esta rutina no la codifica el programador, sino que la aporta el kernel y normalmente tiene como fin el terminar el proceso que recibe la señal. En algunos casos, antes de eliminar al proceso, el kernel se encarga de generar en el directorio de trabajo actual del proceso un fichero llamado core que contiene un volcado de memoria del contexto del proceso. Analizando dicho fichero se podrá saber en qué punto terminó el proceso y por qué motivo se le envió la señal.

3.-        Invocar a una rutina que se encarga de tratar la señal y que ha sido creada por el programador. Esta rutina establecerá un mecanismo de comunicación entre procesos o modificará el curso normal del programa. En estos casos, el proceso no va a terminar a menos que la rutina de tratamiento indique lo contrario.

La siguiente figura pretende reflejar los tres tipos de tratamientos que puede recibir una señal. La primera señal que recibe no provoca que el proceso cambie el curso de su ejecución, esto es debido a que la acción que está activa es que el proceso ignore la señal. El proceso prosigue su ejecución y recibe una segunda señal que le fuerza a entrar en una rutina de tratamiento. Esta rutina, después de tratar la señal, puede optar por tres acciones: restaurar la ejecución del proceso al punto donde se produjo la interrupción, finalizar el proceso o restaurar alguno de los estados pasados del proceso y continuar la ejecución desde ese punto. El proceso puede también recibir una señal que le fuerce a entrar en la rutina de tratamiento por defecto.

 Algunas señales importantes <signal.h>:

SIGTERM = Finalización controlada. Se envía para indicarle a un proceso que debe acabar su ejecución. Puede ser ignorada. Definiremos una rutina para definir el comportamiento del sistema si recibe esta señal.
SIGKILL = Finalización abrupta. No se puede ignorar
SIGINT = Interrupción. Se envía cuando se pulsa la tecla de interrupción (Ctrl+C). Por defecto, se interrumpe el programa.
SIGCLD = Terminación de algún proceso hijo. Se envía al proceso padre. Ignorada por defecto
OTRAS: SIGTRAP, SIGIOT, SIGEMT, SIGFPE,
SIGBUS, SIGSEGV, SIGSYS, SIGPIPE, SIGALRM,
SIGPWR…

Señales disponibles para el programador:

SIGUSR1 y SIGUSR2. Son señales reservadas para el uso del programador. Ninguna aplicación estándar va a usarla y su significado es el que quiera definir el programador en su aplicación. Por defecto tienen la acción de terminar el proceso.
Para ver una cadena de texto sobre una señal:
#include <string.h>
#include <signal.h>
char *strsignal(int sig);

ENVÍO DE SEÑALES!

Para enviar una señal a un proceso o grupo de procesos:
#include <signal.h>
int kill(pid_t pid, int sig);
int killpg(int pgrp, int sig);

Para enviar una señal a sí mismo:

#include <signal.h>
int raise(int sig);

Nota: Si pid > 0, es el PID del proceso al
que enviamos la señal
Si pid = 0, la señal es enviada a todos
procesos del grupo
Si pid = -1, la señal es enviada a los
procesos cuyo id. Real es igual al id.
Efectivo del proceso que la envía.

Tratamiento de Señales:

Para especificar qué tratamiento debe realizar un proceso al recibir una señal. Sig será el valor de la señal, y *action es la acción que se tomará al recibir la señal y puede tomar tres clases de valores:

#include <signal.h>
void (*signal (int sig, void (*action) ())) (); action puede ser:
IG_DFL = acción del manejador por defecto. Por lo general, es la de abortar e incluso dar core.
SIG_IGN = la señal se debe ignorar; Dirección a la rutina de tratamiento.

Ejemplo Práctico:

Vamos a crear un programa que intercepte la señal generada al pulsar las teclas ctrl-C.
Para ello, programamos la función que va a manejar la interrupción, y hacemos un printf de “esperando a ctrl-c”.
Ver error de primera ejecución y por qué. Podemos solventarlo??????
No es el caso de SIGILL, SIGTRAP y SIGPWR, pues no hay tratamientos por defecto a estas rutinas.

Espera:

#include <unistd.h>
int pause(void);
pause deja al proceso suspendido en espera por una señal (cualquiera, Pause devuelve un -1 cuando le llega la interrupción y pone errno a EINTR

Ejemplo Práctico Espera:

Ejercicio práctico:
Vamos a utilizar SIGUSR1 para que un programa genere un número aleatorio cada vez que reciba dicha señal. Usaremos SIGINT para inhabilitar la señal mientras se atiende la interrupción. La señal la daremos desde la consola del sistema mediante:
kill -s USR1 pid, Para finalizar, enviaremos un SIGTERM desde consola.

Para esperar por una señal concreta:

#include <unistd.h>
int sigsuspend (const sigset_t *mask);

Funciones para Señales:

Una señal puede ser tratada o estar pendiente (en espera de ser tratada), un proceso puede bloquear una señal; si se recibe quedará pendiente. La señal deja de estar pendiente cuando el proceso la desbloquea o cambia la rutina de tratamiento a la rutina por defecto sigaction permite instalar manejadores de señales sigprocmask permite especificar un conjunto de señales que queremos que el proceso bloquee  sigpending devuelve el conjunto de señales que están bloqueadas y actualmente pendientes.

Máscaras de señales:

sigprocmask ( int how, const sigset_t *set, sigset_t *oset); how:

SIG_BLOCK (bloquear)
SIG_UNBLOCK (desbloquear)
SIG_SETMASK (poner la máscara al
valor expresado en set)
Si *set es NULL, no se realizan cambios

Conjuntos de señales

Para cierta funciones (p.ej. sigaction) necesitamos especificar un conjunto de señales: sigset_t = unsigned long
#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigmember(const sigset_t *set, int signo);

Manejador de señales

sigaction es más nueva que signal, y es definida por POSIX. Permite ver o modificar (o ambas cosas) la acción asociada a una señal
#include <signal.h>
struct sigaction {
void (*sa_handler)(int); /* dir. del manejador de señal o SIG_IGN o SIG_DFL */
sigset_t sa_mask; /* señales adicionales a bloquear cuando se maneje ésta*/
int sa_flags;
void (*sa_restorer)(void); /* obsoleto */
}
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

Funciones de tiempo:

Cada proceso puede tener hasta tres temporizadores; se ponen con setitimer alarm genera una señal SIGALRM tras un  número de segundos. La acción por defecto de SIGALRM es eliminar el proceso sleep y usleep suspenden al proceso un nº de segundos o. microsegundos.

Esta entrada fue publicada en Consultas. Guarda el enlace permanente.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s