Señales - Braulio J. Solano Rojas

Anuncio
Señales
CI-2400 Programación Avanzada en
Sistemas de Tipo UNIX
Prof. Braulio José Solano Rojas
ECCI, UCR
Señales
●
●
Las señales son interrupciones de software que
pueden ser enviadas a un proceso para
informarle de algún evento asíncrono o
situación especial. El término señal se emplea
también para referirse al evento.
Los procesos pueden enviarse señales unos a
otros a través de la llamada kill y es bastante
frecuente que, durante su ejecución, un
proceso reciba señales procedentes del núcleo.
2 de 77
Señales
●
Cuando un proceso recibe una señal puede
reaccionar de tres formas diferentes:
●
●
●
Ignorar la señal, con lo cual es inmune a la misma.
Invocar a la rutina de tratamiento por defecto. Esta
rutina no la codifica el programador, sino que la
aporta el núcleo. Según el tipo de señal, la rutina
de tratamiento por defecto realizará una acción u
otra. Por lo general suele provocar la terminación
del proceso mediante una llamada a exit.
Invocar a una rutina propia que se encarga de
tratar la señal.
3 de 77
Tipos de señales
●
●
●
Señales relacionadas con la terminación de
procesos.
Señales relacionadas con las excepciones
inducidas por los procesos.
Señales relacionadas con los errores
irrecuperables originados en el transcurso de
una llamada al sistema.
4 de 77
Tipos de señales
●
●
●
Señales originadas desde un proceso que se
está ejecutando en modo usuario.
Señales relacionadas con la interacción con el
terminal.
Señales para ejecutar un proceso paso a paso.
Son usadas por los depuradores.
5 de 77
Señales UNIX System V
●
●
Están definidas en el archivo de encabezados
<signal.h>.
Hay 19 señales definidas por UNIX System V.
6 de 77
SIGHUP
●
Desconexión. Es enviada cuando un terminal
se desconecta de todo proceso del que es
terminal de control. También se envía a todos
los procesos de un grupo cuando el líder del
grupo termina su ejecución. La acción por
defecto de esta señal es terminar la ejecución
del proceso que la recibe.
7 de 77
SIGINT
●
Interrupción. Se envía a todo proceso asociado
con un terminal de control cuando se pulsan la
teclas de interrupción Ctrl+c. Su acción por
defecto es terminar la ejecución del proceso
que la recibe.
8 de 77
SIGQUIT
●
Salir. Similar a SIGINT, pero es generada al
pulsar la tecla de salida Control+\. Su acción
por defecto es generar un fichero core y
terminar el proceso.
9 de 77
SIGILL
●
Instrucción ilegal. Es enviada cuando el
procesador detecta una instrucción que no
forma parte de su repertorio. En los programas
escritos en C suele producirse este tipo de
error cuando manejamos punteros a funciones
que no han sido correctamente inicializados.
Su acción por defecto es generar, un fichero
core y terminar el proceso.
10 de 77
SIGTRAP
●
Trace trap. Cuando un proceso se está
ejecutando paso a paso, esta señal es enviada
después de ejecutar cada instrucción. Es
empleada por los pro­gramas depuradores. Su
acción por defecto es generar un fichero core
-terminar el proceso.
11 de 77
SIGIOT
●
I/O trap instruction. Se envía cuando se da un
fallo de hardware. La naturaleza de este fallo
depende de la máquina. También es enviada
cuando llamamos a la función abort. que
provoca el suicidio del proceso generando: un
fichero core.
12 de 77
SIGEMT
●
Emulator trap instruction. También indica un
fallo de hardware. Raras veces se utiliza. Su
acción por defecto es generar un fichero core y
terminar el proceso.
13 de 77
SIGFPE
●
Error en coma flotante. Es enviada cuando el
hardware detecta un error coma flotante, como
el uso de número en coma flotante con un
formato desconocido, errores de
desbordamiento, etc. Su acción por defecto es
generar un fichero core y terminar el proceso.
14 de 77
SIGKILL
●
Terminación abrupta. Esta señal provoca
irremediablemente la terminación del proceso.
No puede ser ignorada y siempre que se recibe
se ejecuta su acción por defecto, que consiste
en generar un fichero core y terminar el
proceso.
15 de 77
SIGBUS
●
Error de bus. Se produce cuando se da un
error de acceso a memoria. Las dos
situaciones típicas que la provocan suelen ser
intentar acceder a una dirección que
físicamente no existe o intentar acceder a una
dirección impar, violando así las reglas de
alineación que impone el procesador. Su acción
por defecto es generar un fichero core y
terminar el proceso.
16 de 77
SIGSEGV
●
Violación de segmento. Es enviada a un
proceso cuando intenta acceder a datos que se
encuentran fuera de su segmento de datos.
Su acción por defecto es generar un fichero
core y terminar el proceso.
17 de 77
SIGSYS
●
Argumento erróneo en una llamada al sistema.
No se usa.
18 de 77
SIGPIPE
●
Intento de escritura en una tubería de la que no
hay nadie leyendo. Esa suele ocurrir cuando el
proceso de lectura termina de una forma
anormal. Su acción por defecto es terminar el
proceso.
19 de 77
SIGALRM
●
Despertador. Es enviada a un proceso cuando
alguno de sus temporizadores descendentes
llega a cero. Su acción por defecto es terminar
el proceso.
20 de 77
SIGTERM
●
Finalización controlada. Es la señal utilizada
para indicarle a un proceso que debe terminar
su ejecución. Esta señal no es tajante como
SIGKILL y puede ser ignorada. Lo correcto es
que la rutina de tratamiento de esta señal se
encargue de tomar las acciones necesarias
para dejar al proceso un estado coherente y a
continuación finalizar su ejecución con una
llamada a exit. Esta señal es enviada a todos
los procesos cuando se emiten las órdenes
shutdown o reboot. Su acción por defecto es
terminar el proceso.
21 de 77
SIGUSR1
●
Señal número 1 de usuario. Esta señal está
reservada para uso del programador. Ninguna
aplicación estándar va a utilizarla y su
significado es el que le quiera dar el
programador en su aplicación. Su acción por
defecto es terminar el proceso.
22 de 77
SIGUSR2
●
Señal número 2 de usuario. Su significado es
idéntico al de SIGUSR1.
23 de 77
SIGCLD
●
Terminación del proceso hijo. Es enviada al
proceso padre cuando alguno de sus procesos
hijo termina. Esta señal es ignorada por
defecto.
24 de 77
SIGPWR
●
Fallo de alimentación. Esta señal tiene diferentes
interpretaciones. En algunos sistemas es enviada
cuando se detecta un fallo de alimentación y le
indica al proceso que dispone tan sólo de unos
instantes antes de que se produzca la caída del
sistema. En otros sistemas, esta señal es enviada,
después de recuperarse de un fallo de
alimentación, a todos aquellos procesos que
estaban en ejecución y que se han podido
rearrancar. En estos casos, los procesos deben
disponer de mecanismos para restaurar las
posibles pérdidas producidas durante la caída de
la alimentación.
25 de 77
Envío de señales
●
Para enviar una señal desde un proceso a otro
o a un grupo de procesos, emplearemos la
llamada kill:
#include <signal.h>
int kill (pid_t pid, int sig);
donde pid identifica el conjunto de procesos al
que queremos enviarle la señal.
26 de 77
pid
●
Este parámetro es un número entero y los
distintos valores que puede tomar tienen los
siguientes significados:
●
●
pid > 0, es el PID del proceso al que le enviamos la
señal.
pid = 0, la señal es enviada a todos los procesos
que pertenecen al mismo grupo que el proceso que
la envía.
27 de 77
pid
●
●
pid = -1, la señal es enviada a todos aquellos
procesos cuyo identificador real es igual al
identificador efectivo del proceso que la envía. Si
el proceso que la envía tiene identificador efectivo
de superusuario, la señal es enviada a todos los
procesos, excepto al proceso 0 —swapper— y al
proceso 1 —init—.
pid < -1, la señal es enviada a todos los procesos
cuyo identificador de grupo coincide con el valor
absoluto de pid.
28 de 77
sig
●
El parámetro sig es el número de la señal que
queremos enviar. Si sig vale 0 —señal nula—
se efectúa una comprobación de errores, pero
no se envía ninguna señal. Esta opción se
puede utilizar para verificar la validez del
identificador pid.
29 de 77
Ejemplo: kill-1.c
raise
●
Si queremos que un proceso se envíe señales a sí mismo,
podemos usar la llamada raise:
#include <signal.h>
int raise (int sig) ;
donde sig es el número de la señal que queremos enviar; raise
se puede codificar a partir de kill de la siguiente forma:
int raise (int sig)
{
return kill (getpid (), sig);
}
31 de 77
Tratamiento de señales
●
Para especificar qué tratamiento debe realizar
un proceso al recibir una señal, se emplea la
llamada signal:
#include <signal.h>
void (*signal (int sig, void (*action) ())) ();
Ejemplo: signal-1.c
En espera de señales
●
En ocasiones puede interesar que un proceso
suspenda su ejecución en espera de que
ocurra algún evento exterior a él. Por ejemplo,
al ejecutar una entrada/salida. Para estas
situaciones nos valemos de la llamada a pause:
#include <unistd.h>
int pause (void);
34 de 77
Ejemplo: pause-1.c
Saltos globales
●
La rutina de tratamiento de una señal puede
hacer que el proceso vuelva a alguno de los
estados por los que ha pasado con
anterioridad. Esto no sólo es aplicable a las
rutinas de tratamiento de señales sino que se
puede extender a cualquier función. Para
realizar esto nos valemos de las funciones
estándar setjmp y longjmp:
#include <setjmp.h>
int set jmp (jmp_ buf env);
void longjmp (jmp_buf env, int val);
36 de 77
Saltos globales
●
La función setjmp guarda el entorno de pila en
env para un uso posterior por de longjmp;
setjmp devuelve el valor 0 en su primera
llamada. El tipo de env, jmp_buf, está definido
en el archivo de encabezado <setjmp.h>.
37 de 77
Saltos globales
●
La función longjmp restaura el entorno guardado
en env por una llamada setjmp. Después de
haberse ejecutado la llamada a longjmp, el flujo de
la ejecución del programa vuelve al punto donde
se hizo la llamada a setjmp, pero en este caso
setjmp devuelve el valor val que hemos pasado
mediante longjmp. Ésta es la forma de averiguar si
setjmp está saliendo de una llamada para guardar
el entorno o de una llamada de longjmp. longjmp
no puede hacer que setjmp devuelva 0, ya que en
el caso de que val tome el valor 0, setjmp
devolverá 1.
38 de 77
Saltos globales
jmp_buf entorno;
int valor;
…
valor=setjmp(entorno);
(valor=0)
(valor=10)
…
longjmp(entorno,10);
39 de 77
Saltos globales
●
Las funciones setjmp y longjmp se pueden ver
como una forma elaborada de implementar una
sentencia goto no local, capaz de saltar desde
una función a etiquetas que están en la misma
o en otra función. Las etiquetas serían los
entornos guardados por setjmp en la variable
env.
40 de 77
Ejemplo: setjmp-1.c
Señales en 4.3BSD
●
●
La interfaz para el manejo de señales fue
diseñada por la Universidad de California en
Berkeley. Las llamadas kill, signal y pause
están disponibles y se incorporan otras para
hacer más versátil el manejo de señales.
Además, el UNIX de Berkeley añade nuevas
señales a las existentes en la interfaz System V
del UNIX de AT&T.
De las señales que implementa el UNIX
System V, SIGPWR no está implementada en
4.3BSD y SIGCLD pasa a llamarse SIGCHLD.
42 de 77
SIGVTALRM
●
Alarma de un temporizador en tiempo virtual.
Indica que un temporizador descendente en
tiempo virtual ha llegado a cero. Su acción por
defecto es terminar el proceso que la recibe.
43 de 77
SIGPROF
●
Alarma de un temporizador. Indica que un
temporizador descendente que cuenta tanto en
tiempo real como virtual, ha llegado a 0. Su
acción por defecto es terminar el proceso que
la recibe.
44 de 77
SIGIO
●
Señal de entrada/salida asíncrona. Indica que
un dispositivo o archivo está listo para una
operación de entrada/salida. Su acción por
defecto es ignorar la señal.
45 de 77
SIGWINCH
●
Cambio del tamaño de una ventana. Se usa en
las interfaces gráficas orientadas a ventanas
como X-WINDOW. Su acción por defecto es
ignorar la señal.
46 de 77
SIGSTOP
●
Señal de parada de un proceso. Esta señal no
proviene de un terminal de control. La señal no
puede ser ignorada ni capturada y su acción
por defecto es parar el proceso.
47 de 77
SIGTSTP
●
Señal de parada procedente de un terminal. Es
generada por el teclado. Su acción por defecto
es parar el proceso.
48 de 77
SIGCONT
●
Continuar. Señal para reanudar las ejecución
de un proceso. Su acción por defecto es
ignorar la señal.
49 de 77
SIGTTIN
●
La reciben los procesos que se ejecutan en
segundo plano y que intentan leer datos de un
terminal de control. Su acción por defecto es
parar el proceso.
50 de 77
SIGTTOU
●
La reciben los procesos que se ejecutan en
segundo plano y que intentan escribir en un
terminal de control. Su acción por defecto es
parar el proceso.
51 de 77
SIGURG
●
Indica que ha llegado un dato urgente a través
de un canal de entrada/sa­lida. Su acción por
defecto es ignorar la señal.
52 de 77
SIGXCPU
●
Le indica al proceso que la recibe que ha
superado su tiempo de CPU asignado. Su
acción por defecto es terminar el proceso.
53 de 77
SIGXFSZ
●
Le indica al proceso que la recibe que ha
superado el tamaño máximo del fichero que
puede manejar. Su acción por defecto es
terminar el proceso.
54 de 77
Tratamiento de señales
●
La llamada signal sigue disponible en el
sistema 4.3BSD, pero además disponemos de
otra llamada para especificar la forma de tratar
una señal. Esta llamada es sigvec y su
declaración es la siguiente:
#include <signal.h>
sigvec (int sig, struct sigvec *vec, struct sigvec
*ovec);
55 de 77
Tratamiento de señales
●
Tanto vec como ovec son punteros a
estructuras del tipo sigvec. Esta estructura
está definida con los siguientes campos:
struct sigvec {
void (*sv_handler) ();
long sv_mask;
long sv_flags;
}
56 de 77
Ejemplo: sigvec-1.c
Ejemplo: sigvec-2.c
Ejemplo: sigvec-3.c
Protección de zonas críticas
●
En ocasiones puede interesarnos proteger
determinadas zonas de código contra la llegada
de alguna señal. Para realizar esto nos
valdremos de las llamadas sigsetmask y
sigblock:
#include <signal.h>
long sigsetmask (long mask);
long sigblock (long mask);
Protección de zonas críticas
●
La función sigsetmask fija la máscara de
señales actual. Esta máscara indica qué
señales tienen bloqueada su recepción. La
señal número i está bloqueada si el i-ésimo bit
de mask vale 1. Este bit lo podemos fijar con la
macro sigmask(i). Naturalmente, las señales
que no pueden ser ignoradas ni capturadas
tampoco van a poder ser bloqueadas.
Protección de zonas críticas
●
●
La función sigblock permite añadir a la máscara
de señales actual aquellas señales que se
especifiquen con el parámetro mask. Así, con
sigblock podemos modificar la máscara fijada
por sigsetmask.
La diferencia entre sigsetmask y sigblock es
que la primera fija la máscara de forma
absoluta y, la segunda, de forma relativa. Las
siguientes líneas de código aclaran este
concepto.
62 de 77
En espera de señales
●
La llamada sigpause permite parar un proceso
hasta que se reciba alguna de las señales
deseadas. Si con pause podíamos parar un
proceso en espera de la primera señal que la
reciba, con sigpause podemos seleccionar la
señal por la que esperamos. La declaración de
esta función es:
#include <signal.h>
long sigpause (long mask);
63 de 77
Gestión de señales POSIX
●
Además de los gestores de señales descritos,
hay otros estándares adoptados por muchos
fabricantes de sistemas UNIX. En concreto, el
estándar POSIX 1003.1 propone un gestor de
señales con una funcionalidad muy similar a la
de 4.3BSD y que ha sido adoptada por Linux,
4.4BSD y FreeBSD.
64 de 77
Tratamiento de señales
●
Con la llamada sigaction asociamos señales
con acciones para indicar cómo responderá el
proceso actual cuando reciba una señal. La
declaración de sigaction es:
#include <signal.h>
int sigaction (int sig, const struct sigaction *act,
struct sigaction *oact);
65 de 77
Estructura sigaction
struct sigaction {
// Si el indicador SA_SIGINFO no está activo.
void (*sa_handler) (int signo);
// Si el indicador SA_SIGINFO está activo.
void (*sa_sigaction) (int signo, siginfo_t *info, void
*context);
sigset_t sa_mask; // Máscara de señales. Indica las
señales adicionales
// que serán bloqueadas durante la ejecución del
// manejador.
int sa_flags; // Indicadores.
}
66 de 77
Estructura siginfo_t
typedef struct {
int si_signo; // Número de la señal.
int si_errno; // Número de error asociado con la señal.
int si_code; // Código que especifica la causa de la señal.
pid_t si_pid; // PID del proceso que envía la señal —señal SIGCHLD—.
uid_t si_uid; // UID del proceso que envía la señal —señal SIGCHLD—.
void *si_addr; // Dirección de la instrucción -señales SIGILL y SIGFPE// o del dato -señales SIGBUS y SIGSEGV- ilegal que
// origina la señal.
int si_status; // Valor de salida o número de señal del poceso hijo
// -señal SIGCHLD-.
long si_band; // Datos del evento -señal SIGPOLL-.
unión sigval si_value; // Datos de la señal —señales de
// tiempo real [SIGRTMIN, SIGRTMAX]-.
} siginfo_t;
67 de 77
Envío de señales
●
●
En POSIX también podemos usar la llamada
kill para enviar señales.
Los sistemas que implementan las extensiones
de tiempo real disponen de la llamada sigqueue
para enviar señales acompañadas de
información:
#include <signal.h>
int sigqueue (pid_t pid, int signo, const union
sigval value)
68 de 77
Envío de señales
●
Para realizar envío de señales entre hilos de un mismo proceso
se emplea la función pthread_kill:
#include <signal.h>
int pthread_kill (pthread_t thread, int sig);
donde thread es el hilo al que le enviamos la señal y sig es el
número de la señal enviada. Nuevamente se tendrá en cuenta
que SIGKILL afecta a todo el proceso y, por lo tanto. para
terminar la ejecución de un hilo concreto utilizaremos
pthread_cancel.
69 de 77
Máscara de señales
●
Para manipular conjuntos de señales hay que
empezar inicializándolos con sigfillset y
sigemptyset:
#include <signal.h>
int sigfillset (sigset_ t *set);
int sigemptyset (sigset_t *set);
70 de 77
Máscara de señales
●
Una vez que se ha inicializado un conjunto
podemos manipularlo para incluir una señal —
sigaddset—, excluirla —sigdelset— o preguntar
si está incluida —sigismember—:
#include <signal.h>
int sigaddset (sigset_t *set, int signo);
int sigdelset (sigset__t *set, int signo);
int sigismember (const sigset_ t *set, int signo);
71 de 77
Máscara de señales
●
A partir de un conjunto podemos modificar la
máscara de señales bloqueadas en un proceso
—sigprocmask— o en un hilo —
pthread_sigmask—:
#include <signal.h>
int sigprocmask (int how, const sigset_ t * set,
sigset_ t *oset);
int pthread_sigmask (int how, const sigset_t *set,
sigset_t *oset);
72 de 77
Parámetro how
SIG_BLOCK
La máscara resultante es el resultado de
la unión de la máscara actual y el
conjunto.
SIG_SETMASK
La máscara resultante es la indicada en el
conjunto.
SIG_UNBLOCK
La máscara resultante es la intersección
de la máscara actual y el com­plementario
del conjunto. Es decir, las señales
incluidas en el conjunto quedarán
desbloqueadas en la nueva máscara de
señales.
73 de 77
Ejemplo: exception.h, exception.c y excep.c
Resumen de gestores de señales
4.3BSD
SYSTEM V
POSIX
signal()
signal()
signal()
kill()
kill()
kill()
pause()
sigpause()
sigsuspend()
signal()
sigvector() ó sigvec()
sigaction()
-
sigblock()
sigprogmask()
-
sigsetmask()
sigprogmask()
-
sigstack()
-
75 de 77
Ejemplos de aplicaciones
que utilizan señales
●
Software tolerante a fallos
●
Sincronización de procesos
76 de 77
¡Gracias por su atención!
¿Preguntas?
Descargar