viernes, septiembre 28, 2012

Señales y gdb

En Unix, las señales son notificaciones asíncronas que recibe un proceso (o hilo dentro del mismo) cuyo origen puede ser otro proceso del sistema, el mismo proceso que las recibe o el núcleo del sistema operativo como consecuencia de una acción que ha realizado el proceso en cuestión durante la ejecución - por ejemplo un acceso a una posición de memoria protegida - . Interrumpe el flujo de ejecución normal, y el proceso que las recibe puede gestionarlas si ha instalado un manejador que se haga cargo de la señal a través del API sigaction o signal. También, usando el mismo API, pueden ser ignoradas, de tal manera que no se pasan al programa.

En un sistema Unix, toda señal está identificada por un número y tiene asociado un nombre que puede verse consultando la página de manual o el fichero de cabecera signal.h. Todas las señales tienen un comportamiento por defecto que es el que sucede cuando un proceso recibe una señal y no se ha ignorado o no se ha instalado un manejador para la misma.

Cuando un proceso está depurándose bajo el control del gdb, es éste el que recibe las señales que van al proceso, y puede decidir parar cuando las recibe, devolviéndonos al prompt del gdb , pasar o no pasar las señales e imprimir información sobre las mismas. El gdb almacena unas tablas, con cada señal que está en el sistema que debe hacer. Estas tablas se manejan con ayuda de la orden handle.

Se puede ver el contenido de toda la tabla de señales con ayuda de la orden info signals o info handle. En este caso, gdb nos volcará una tabla con todas las señales que tenga registrada el sistema, si el programa debe o no detenerse, si debe de imprimir información por la consola del gdb y si esta señal debe de pasarse al programa. Esta orden permite especificar el número de señal (siempre que esté en el rango 1 a 15) o el nombre de la misma , si queremos ver información sólo de las señales que nos interesan. El nombre de todas las señales que reconoce el gdb para la arquitectura que está depurando puede verse en la primera columna de la tabla de señales.

Por ejemplo, si se quiere ver la información sobre la señal SIGTERM:


(gdb) info signal SIGTERM

Signal        Stop    Print    Pass to program    Description

SIGTERM       Yes    Yes    Yes        Terminated

(gdb) 

En este ejemplo puede verse que esta señal
  • Para la ejecución del programa y nos devuelve al prompt del depurador, Stop = Yes.
  • Imprime en pantalla el momento en que se genera, Print = Yes.
  • Se pasa al programa una vez que continuemos la ejecución, Pass = Yes.

Si queremos cambiar el comportamiento del gdb cuando se genera una señal, hay que hacer uso del comando handle. Como argumentos admite una lista de nombres simbólicos de señales, seguido de una serie de tokens que cambia el comportamiento de la misma. Así tenemos stop y nostop que hacen que en caso de que la señal se genere se devuelva el control al gdb, pass (ignore) y nopass (noignore) que hace que la señal no se pase al programa que se esté depurando (las formas entre parénteis son equivalentes) , y por último print y no print para que el depurador imprima la señal que se ha recibido.

Por ejemplo, si se quiere que el gdb imprima un mensaje cada vez que se generan las selañes SIGUSR1 y SIGUSR2 con destino al programa, no queremos que éstas pasen al mismo, y queremos que imprima por pantalla el momento que se reciben, se puede hacer con:


(gdb) handle SIGUSR1 SIGUSR2 nopass nostop print

Signal        Stop    Print    Pass to program    Description

SIGUSR1       No    Yes    No        User defined signal 1

SIGUSR2       No    Yes    No        User defined signal 2


Una particularidad es que en algunas arquitecturas el gdb es capaz de mostrar información que el kernel ha pasado al manejador de señales. Esta información se almacena en la seudovariable $_siginfo, cuya estructura puede verse con la orden ptype $_siginfo.


(gdb) ptype $_siginfo
type = struct {
    int si_signo;
    int si_errno;
    int si_code;
    union {
        int _pad[28];
        struct {...} _kill;
        struct {...} _timer;
        struct {...} _rt;
        struct {...} _sigchld;
        struct {...} _sigfault;
        struct {...} _sigpoll;
    } _sifields;
}


Technorati Tags: ,

No hay comentarios: