Sie sind auf Seite 1von 38

El mecanismo de se nales en el sistema operativo Linux.

*
Mayo, 2009
Abstract. El modelo de c omputo conocido como se nales y utilizado en los sistemas operativos UNIX para comunicar eventos relevantes a los procesos es el objeto de estos apuntes. El material se centra en la arquitectura del sistema operativo Linux.

1.

Introducci on.

El concepto de se nal permite la utilizaci on de un modelo de c omputo as ncrono por el cual, los procesos pueden reaccionar a eventos provocados por ellos mismos o por otro proceso [Stallings, 2001]. Podemos asimilar las se nales a las interrupciones hardware, aunque se mantienen notables diferencias, por ejemplo, no se emplea un mecanismo de prioridades. Cada se nal se corresponde con un evento particular. La se nal se entrega actualizando un campo del descriptor del proceso destinatario de la misma. Es importante destacar que la se nal no es atendida de forma s ncrona, sino que el proceso se hace cargo de la misma en momentos puntuales. En algunos sistemas esto sucede justo cuando el proceso despierta, y en otros, como Linux, al regresar de una interrupci on o excepci on. Cada se nal tiene asociada una acci on. Existe un conjunto de acciones predeterminadas, asociadas por defecto a las se nales posibles. En el sistema Linux, las acciones predeterminadas m as frecuentes son: Ignorar la se nal.
*

Sistemas operativos (2008-2009). Este documento puede ampliarse a lo largo del curso.

Terminar el programa. Terminar el programa y generar un archivo core. La acci on predeterminada para un proceso puede ser modicada mediante la especicaci on de una funci on de captura o interceptaci on de la se nal (handler ). En Unix existen dos excepciones a esta posibilidad que son la se nal SIGKILL y SIGSTOP, lo que permite que el superusuario del sistema est e en disposici on de interrumpir o suspender la ejecuci on de cualquier proceso. Adem as, hay dos se nales que est an a disposici on del programador, no quedando asociadas a ning un evento en particular, son SIGUSR1 y SIGUSR2. En UNIX, cada se nal se representa por un nombre con la estructura SIGXXX. Se puede consultar el n umero de se nales que nuestro sistema puede tratar mediante el comando de la shell kill -l o bien consultando el archivo de cabecera <signal.h>. En UNIX, las se nales pueden generarse de divesas formas, por ejemplo: Resultado de una excepci on del hardware. Por ejemplo, cuando un proceso trata de escribir en una zona de memoria no asignada se produce un acceso inv alido. Se produce entonces una excepci on atrapada por el n ucleo que genera la se nal SIGSEGV que se env a al proceso transgresor. Pulsaci on de las teclas Ctrl-C en un terminal. Se genera la se nal SIGINT cuya acci on predeterminada es terminar el proceso de primer plano de la sesi on. Proviene de una llamada del sistema kill que nos permite enviar una se nal a un proceso predeterminado. Se trata de un evento gestionado por el n ucleo del sistema. Por ejemplo, la se nal SIGALARM es emitida cuando expira una alarma, y es enviada al proceso que la ha solicitado.

2.

Introducci on al sistema de se nales en LINUX.

Linux implementa un conjunto de se nales fruto de la uni on de varios est andares: POSIX fundamentalmente, Unix System V y BSD. En la siguiente tabla tenemos una parte de las se nales, indic andose si son conforme POSIX o no, mediante el s mbolo *: 2

Se nal SIGHUP * SIGINT * SIGQUIT * SIGABRT *, SIGIOT SIGKILL * SIGTERM * SIGILL * SIGTRAP SIGBUS SIGFPE * SIGSEGV * SIGTKFLT SIGUSR1 SIGUSR2 SIGPIDE* SIGCHLD*, SIGCLD SIGCONT * SIGSTOP * SIGTSTP * SIGTTIN * SIGTTOU * SIGXCPU SIGXFSZ SIGALRM * SIGVTALRM SIGPROF SIGWINCH SIGIO, SIGPOLL SIGURG SIGPWR,SIGINFO

Acci on por defecto Terminaci on Terminaci on Terminaci on con core Terminaci on con core Terminaci on Terminaci on Terminaci on Terminaci on con core Terminaci on Terminaci on Terminaci on con core Terminaci on Terminaci on Terminaci on Terminaci on Ninguna Proseguir si estaba parado Suspensi on Suspensi on Suspensi on Suspensi on Terminaci on Terminaci on Terminaci on Terminaci on Terminaci on Ninguna Terminaci on Ninguna Terminaci on

Grupo Terminaci on proceso Terminaci on proceso Terminaci on proceso Terminaci on proceso Terminaci on proceso Terminaci on proceso Excepci on de hardware Excepci on de hardware Excepci on de hardware Excepci on de hardware Excepci on de hardware Excepci on de hardware Usuario Usuario Cierre de tuber a Suspensi on o Continuaci on Suspensi on o Continuaci on Suspensi on o Continuaci on Suspensi on o Continuaci on Suspensi on o Continuaci on Suspensi on o Continuaci on Recursos modicables Recursos modicables Gesti on de alarmas Gesti on de alarmas Gesti on de alarmas Entrada / Salida Entrada / Salida Entrada / Salida Fallo alimentaci on (con SAI)

Las se nales mostradas en la tabla se denominan se nales est andar. Hay 32 se nales m as denominadas se nales de tiempo - real. Mientras que en las primeras no puede haber m as de una instancia de cada se nal pendiente para ser tratada en una cola, las se nales de tiempo - real admiten m as de una instancia. El destinatario de una se nal en Linux 2.6 puede ser: Un solo LWP (light weight process). Recordemos que un LWP puede considerarse como un tipo de proceso especial que conforma una unidad en Linux 2.6, ya que siendo capaz de compartir con otros procesos los recursos, constituye una unidad b asica para el planicador y al mismo tiempo permite realizar implementaci on de procesos multihilo. Si un proceso est a formado por un s olo LWP, cuando enviamos una se nal al PID del proceso mediante una funci on como kill, la se nal le llegar a s olo a este LWP. Si el proceso est a formado por varios LWP (thread group), a un podemos enviar la se nal a s olo un LWP mediante las funciones tkill o tgkill (ver m as abajo), indicando el PID del LWP particular al que se desee enviar la se nal. Un thread group o conjunto de LWPs que constituyen un proceso. En este caso el PID del proceso es en realidad el TGID de los LWPs que lo componente, que resultar a igual al PID del LWP l der del grupo. Al enviar una se nal a un proceso constituido por un thread group, todos los LWP del thread group recibir an la se nal. 3

Un grupo de procesos. La funci on killgp() permite enviar una se nal a todos los procesos que forman un grupo de proceso. La vida de una se nal se descompone en dos fases: la generaci on y la entrega [Bover y Cesati, 2005]. Una se nal se dice generada por un proceso cuando ocurre el evento asociado a la se nal. Una se nal es entregada cuando el proceso al que se le envi o lleva a cabo la acci on relacionada con ella. Entre la generaci on y la entrega, se la considera pendiente. Por otra parte, la entrega de una se nal puede ser bloqueada. La entrega se retrasar a hasta que la se nal sea desbloqueada o hasta que el proceso receptor pasa a la situaci on de ignorar la se nal. La situaci on de ignorar es una de las posibles disposiciones de un proceso respecto a una se nal. Las tres posibles disposiciones son: que se produzca una acci on predeterminada, que se ignore y que se produzca la captura de la misma. Esta u ltima disposici on consiste en ejecutar un c odigo asociado a la se nal en lugar de la acci on establecida por defecto. En linux existen estructuras de datos especiales para manejar las se nales mediante el uso de llamadas del sistema. Una estructura de uso frecuente en la gesti on de se nales wa el conjunto de se nales (sigset_t ). Un conjunto de se nales es un tipo de dato sigset t denido en <signal.h>, capaz de representar m ultiples se nales. El conjunto de se nales se suele utilizar para denir m ascaras que establecen por ejemplo el conjunto de se nales bloqueadas. Los sistemas operativos que como Linux utilizan el modelo de c omputo de se nales suelen tener un conjunto de llamadas del sistema que facilitan la gesti on de las mismas.

3.

El sistema de se nales en Linux desde el punto de vista del programador.

Antes de pasar a ver en mayor detalle funciones y llamadas del sistema relacionadas con las se nales, es conveniente tener en mente el siguiente resumen de conceptos para el programador de aplicaciones [Wall, 2001]. No contiene toda la informaci on, especialmente en lo que se reere a las llamadas por lo que es necesario tambi en estudiar la siguiente secci on que est a dedicada integramente a las llamadas del sistema. Denici on. Se nal. Una se nal puede entenderse como una interrupci on software que se genera como consecuencia de un evento s ncrono o as ncrono 4

o de alguna situaci on especial controlada por el sistema operativo. Sin embargo el tratamiento de la se nal no es igual al de una interrupci on puesto que las se nales dirigidas a un proceso son atendidos s olo en determinados momentos, como por ejemplo, tras volver de una excepci on o interrupci on. De esta manera, el tratamiento de la se nal no se realiza de forma s ncrona, a pesar que el evento que la ha generado pueda ser s ncrono o as ncrono. Un evento s ncrono sucede como consecuencia de la acci on de nuestro proceso, por ejemplo un intento de acceder a una posici on ilegal de memoria. El evento as ncrono se produce al margen de la ejecuci on del proceso, como por ejemplo la pulsaci on de una combinaci on de teclas predeterminada en una terminal. Denici on. Generaci on de una se nal. La generaci on de una se nal es la ocurrencia del evento asociado a la misma y el proceso por el cu al el kernel deja constancia del mimo, mediante la inclusi on de un nuevo elemento en una cola de se nales pendientes y/o el ajuste de determinadas variables en el proceso de destino. Denici on. Entrega de la se nal. La entrega de la se nal es el proceso por el cu al el kernel, procede a intentar realizar la acci on asociada a la se nal establecida como pendiente. Posteriormente veremos en detalle desde el punto de vista del kernel el proceso de generaci on y entrega de la se nal. Denici on. Ignorar una se nal . La se nal puede ser ignorada. Este hecho puede darse tanto en la fase de generaci on, d onde el kernel renuncia a dejar constancia del evento, como en la fase de entrega, donde la acci on a realizar es omitida. La excepci on la constituyen las se nales SIGKILL y SIGSTOP. Denici on. Se nal bloqueada . Una se nal est a bloqueada cuando el proceso destinatario de la se nal la marca como bloqueada. En esta situaci on el proceso destinatario no realiza la acci on asociada a la entrega de la se nal. Esta situaci on termina cuando se procede al desbloqueo de la se nal o bien se modica el tratamiento para que la se nal sea ignorada. Denici on. Tipo de datos sigset t en Linux . Este tipo de datos representa una doble palabra donde cada bit est a asociado a una se nal. En Linux existen un conjunto de funciones para modicar este tipo de dato: int sigemptyset(sigset t *set). Esta funci on pone a 0 todos los bits de 5

la doble palabra apuntada por set. int sigllset(sigset t *set). Esta funci on pone a 1 todos los bits de la doble palabra apuntada por set. int sigaddset(sigset t *set, int signo). Se realiza la operaci on *set=*set+signo. int sigdelset(sigset t *set, int signo). Se realiza la operaci on *set = *set - signo. int sigmember(sigset t *set, int signo). Devuelve 1 si signo est a en set y 0 en caso contrario. Denici on. Llamadas del sistema en Linux relacionadas con la gesti on de las se nales. int sigprocmask(int how, sigset t *set, sigset t *old). Permite modicar la m ascara de se nales bloqueadas de un proceso. El argumento how determina como debe ser modicada la m ascara: SIG BLOCK indica mask=mask + *set, SIG UNBLOCK indica mask=mask-*set, SIG SETMASK indica mask=*set. Si set es NULL la funci on no realiza la modicaci on de la m ascara, sino que lo que la consulta. El resultado de la consulta lo coloca en old. int kill(int pid, int signo). Esta funci on del sistema genera una se nal cuyo n umero est a determinado por el par ametro signo siendo el proceso afecta aqu el cuyo identicador es pid. M as abajo describimos en mayor detalle esta funci on. int alarm(int seconds). La llamada del sistema alarm sirve para programar un temporizador en un proceso. El par ametro seconds establece el tiempo que queda para producir una se nal de alarma cuyo destinatario es el proceso. Devuelve el n umero de segundos que quedan. int sigaction(int signo, struct sigaction *act, struct sigaction *old). La llamada del sistema sigaction permite establecer un c odigo para el tratamiento de la se nal. El par ametro signo establece el n umero de se nla, para la que el proceso va a disponer un c odigo de tratamiento. Si act no es nulo, se trata de un puntero a una estructura sigaction donde se describe el nuevo tratamiento. Por contra si act NULL, el efecto de esta llamada es consultar la estructura sigaction asociada a esta se nal en el proceso. El resultado se devuelve en old. 6

int sigsuspend(sigset t *set). Esta llamada del sistema bloquea el conjunto de se nales establecido en set y espera a que nalice el tratamiento de alguna de las se nales no bloquedas. Esta funci on es at omica. Cuando la funci on vuelve se restaura el valor original de la m ascara de se nales bloqueadas del proceso. Devuelve -1 en caso de error y en errno se coloca EINTR. La llamada del sistema sigaction comentada hace uso de una estructura de datos especial del tipo struct sigaction. Esta estructura est a formada por tres campos: El primero es void (*sa handler)(). Cuando se trata de un puntero a una funci on, se interpreta como la funci on de tratamiento de la se nal establecida para el proceso. Tambi en puede tomar el valor SIG DFL, en cuyo caso el comportamiento debe ser el establecido por defecto para la se nal, o bien SIG IGN, en el caso de que se desee ignorar la se nal. El segundo es sigset sa mask. Esta es la m ascara de se nales a bloquear mientras se est a produciendo el tratamiento de la se nal, que se suman a las que ya lo est an. El tercero es int sa ags. Este campo permite modicar el comportamiento por defecto de la se nal en algos aspectos. Salvo que se especique lo contrario las funciones anteriores devuelven 0 si no se produce error y 1 en caso contrario. Tras este resumen veremos de forma m as extensa la utilizaci on de estas funciones.

3.1.
3.1.1.

Llamadas al sistema b asicas.


Envio de se nal. Llamada kill.

La llamada al sistema kill se utiliza para enviar una se nal a un thread group, un grupo de procesos o a conjuntos de thread groups. Existe tambi en un comando de la shell denominado kill, que act ua como interfaz de usuario ante esta llamada.
#include <signal.h> int kill(pid_t pid, int sig);

El primer argumento indica la identidad del destinario. Admite varias posibilidades: 7

Si pid es positivo, se genera la se nal sig. Si pid es 0, la se nal sig se env a a los procesos hijos del proceso emisor. Si pid es -1, la se nal sig se env a a todos los procesos, salvo al primero (programa (init)). Si pid es inferior a -1, la se nal sig se env a al grupo de procesos indenticado por el valor -pid Para que kill pueda realizar su trabajo, es necesario que el identicador de usuario real o efectivo del proceso emisor sea el mismo que el del proceso receptor. Los procesos del superusuario pueden enviar se nales al resto de los procesos, salvo al proceso init. Nadie puede enviar una se nal al procesos init, con lo que se salvaguarda el sistema. Si la llamada se desarrolla correctamente se devuelve el valor 0. Si se produce un error, se devuelve el valor -1 y la variable errno puede contener alguno de los sigientes valores: EINVAL: la se nal especicada no es v alida. ESRCH: el destinario no existe. EPERM: el valor del identicador de usuario efectivo del proceso que llama es diferente del proceso o proceso destinos. No hay ninguna se nal representada por el entero 0. Esta es la se nal nula y sirve para realizar una comprobaci on de errores normal, sin enviar ninguna se nal. La llamada al sistema kill tiene una funci on de servicio denominada sys kill() que realiza dos subtareas [Bover y Cesati, 2005]. La primera establece una estructura de datos de tipo siginfo t utilizada comunmente como descriptor de se nales. La segunda tarea es realizar una llamada a la funci on kill something info(). Lo podemos ver en el siguiente c odigo:
info.si_signo=sig; info.si_errno=0; info.si_code=SI_USER; info._sifields._kill._pid=current->tgid; info._sifields._kill._uid=current->uid; return kill_something_info(sig, &info, pid);

a a su vez a: La llamada kill something info() llamar 8

kill proc info. Para enviar la se nal a un thread group mediante group send sig info. kill pg info. Para enviar la se nal a un grupo de procesos. Llamadas group send sig info para todos los procesos del sistema si pid es 1 o a todos los procesos hijo del actual si pid es 0 Aunque la llamada del sistema kill() puede enviar las se nales de la 1 a la 64, esto es, incluyendo las se nales real-time, no se puede garantizar que estas u ltimas sean atendidas, debido a que no coloca m ultiples instancias de la misma se nal en la cola de se nales pendientes. Las se nales de tiempo real deben ser enviadas mediante rt sigqueueinfo(), si se desea asegurar este punto. Algunas variantes de kill() son killpg() y raise(). Estas funciones son llamadas del sistema en System V o BSD Unix, pero en Linux son implementadas como funciones de librer a que acaban llamando a kill(). La primera, killpg(), es utilizada para enviar una se nal a un grupo de procesos, y la segunda raise(), env a la se nal al mismo proceso desde el que se hace la llamada a la funci on. Veamos un ejemplo de utilizaci on de la funci on kill:
#include #include #include #include #include #include <sys/types.h> <wait.h> <unistd.h> <signal.h> <stdio.h> <stdlib.h>

int main(void) { pid_t child; int errret; if((chid = fork()) < 0) {perror("fork"); exit(EXIT_FAILURE); } else if(child==0) { // estamos en el proceso hijo sleep(30); }

else {// en el proceso padre printf("enviamos SIGCHLD a %d\n",child); //nos sirve para asegurarnos que child est a vivo. errret=kill(child,SIGCHLD); if(errret<0) perror("kill:SIGCHLD"); else printf("%d est a todav a vivo\n", child); //ahora eliminamos al proceso hijo printf("eliminando %d\n",child); if((kill(child,SIGTERM))<0) perror("kill:SIGTERM"); waitpid(child,NULL,0); //tiene que esperar el estado } exit(EXIT_SUCCESS); }

3.1.2.

Llamadas para enviar una se nal a un thread espec co.

Mientras kill() env a una se nal a un thread group, a un grupo de procesos, o a todos los thread groups del sistema, las llamadas tkill() y tgkill() sirven para enviar una se nal a un thread espec co dentro de un thread group. La llamada del sistema tkill() espera dos par ametros: el pid del proceso al que se va a enviar la se nal y el n umero de la se nal. La rutina de servicio on del sys tkill() rellena una estructura de tipo siginfo t, calcula la direcci descriptor del proceso, realiza el chequeo de permisos, e invoca la funci on specic send sig info() para realizar la generaci on de la se nal. La llamada del sistema tgkill() tiene un tercer par ametro, el id del thread group al que se supone que debe pertenecer el proceso al que se quiere enviar la se nal. Entonces tgkill() realiza las mismas acciones descritas en tkill() pero adem as comprueba que el proceso pertenece al grupo se nalado. Este a nadido se utiliza para resolver una condici on de carrera que puede suceder cuando se env a una se nal a un proceso de un grupo que est a siendo eliminado y existe una aplicaci on que est a creando procesos al mismo tiempo. Podr a suceder que se env ase la se nal al proceso equivocado, si no se realizara esta comprobaci on. 3.1.3. Llamadas para establecer y consultar conjuntos de se nales.

Una parte importante de la gesti on de la se nales se realiza mediante la creaci on y modicaci on de una estructura de datos denominada sigset t. 10

Las funciones para establecer y consultar un conjunto de se nales son:


int int int int int sigemptyset(sigset_t *set); sigfillset(sigset_t *set); sigaddset(sigset_t *set, int signum); sigdelset(sigset_t *set, int signum); sigismember(const sigset_t *set, int signum);

Todos estos prototipos est an denidos en <signal.h>. La primera de las funciones, sigemptyset crea un conjunto de se nales vac o. Por contra, sigllset crea un conjunto de se nales completo. Las funciones sigaddset y sigdelset permiten a nadir y eliminar una se nal del conjunto. Por u ltimo sigismember comprueba si la se nal est a incluida en el conjunt devolviendo un 1 si as ocurre y un 0 en caso contrario. 3.1.4. Consultar las se nales pendientes.

La llamada del sistema sigpending() permite consultar las se nales pendientes bloqueadas del proceso, es decir aquellas que han sido generadas pero estaban bloqueadas. La rutina de servicio se encarga de actualizar una variable de tipo estructura sigset t, con la m ascara de estas se nales. 3.1.5. Bloqueo de se nales.

Un conjunto de se nales permite construir una m ascara en relaci on al bloqueo de se nales. Esto se realiza mediante la funci on sigprocmask. El prototipo es el siguiente:
#include <signal.h> int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

El valor del argumento how establece la acci on a realizar: SIG BLOCK indica que el conjunto set contiene se nales adicionales a bloquear, SIG UNBLOCK indica que el conjunto set contiene se nales adicionales a nales a bloquear. desbloquear y SIG SETMASK indica que set contiene las se Por otra parte si oldset no es nulo, la m ascar anterior se guarda en oldset. 3.1.6. Suspensi on de un proceso.

La llamada del sistema sigsuspend() pasa el proceso al estado es de haber bloqueado un conjunto de TASK INTERRUPTIBLE, despu se nales especicado mediante una m ascara. El proceso despierta s olo cuando una se nal que no est a bloqueada o ignorada le es enviada. 11

Esta llamada puede parecer redundante si se piensa que puede ser sustituida por la secuencia sigprocmask() - sleep(). Sin embargo, pensemos qu e ocurre si sigprocmask() desbloquea una se nal con la idea que sea esta la que permita sacar al proceso del estado TASK INTERRUPTIBLE, y esta se nal es entregada antes de ejecutar sleep(). En este caso el proceso permanecer a en TASK INTERRUPTIBLE. 3.1.7. Llamadas del sistema para se nales real-time.

Las llamadas del sistema para se nales real-time son similares: rt sigaction(), rt sigpending(), rt sigprocmask(), y rt sigsuspend(). Por otra parte: La llamada rt sigqueueinfo() env a una se nal real-time de manera que es a nadida a la cola compartida de se nales pendientes del proceso de destino. Normalmente se invoca mediante la funci on de librer a sigqueue(). nales pendientes a una La llamada rt sigtimedwait() saca de la cola de se se nal bloqueada sin entregarla, y devuelve el n umero de se nal al proceso que hace la llamada. Si no hay se nales bloqueadas pendientes supende el proceso actual por una intervalo de tiempo especicado. Normalmente se invoca desde las funciones de librer a sigwaitinfo() y sigtimedwait(). 3.1.8. Interceptaci on de se nales.

Un proceso puede determinar c omo responder ante cualquier se nal, excepto respecto a SIGSTOP y SIGKILL. En estos casos, el comportamiento predeterminado no puede ser modicado. Adem as muchas veces interesa interceptar la se nal y modicar su comportamiento predeterminado. Consideremos por ejemplo un proceso padre que genera varios procesos hijo. Cuando alguno de ellos es eliminado el padre recibe la se nal SIGCHLD. Esta se nal puede usarse para llamar a wait o waitpid s olo en el momento de la recepci on de la se nal. La llamada a sigaction se emplea para establecer los diferentes aspectos de la disposici on de un proceso ante una se nal. El prototipo es el siguiente:

#include <signal.h> int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)

El par ametro signum es la se nal que se desea interceptar y puede ser cualquiera salvo SIGKILL y SIGSTOP. Si act no es nulo, la nueva acci on es apuntada por act. Si oldact no es nulo, la acci on anterior se guarda en oldact. La estructura sigaction describe la manera en la que el proceso trata la se nal: 12

struct sigaction{ void (*sa_handler)(int); vois (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void);

El primer miembro de la estructura, sa handler, es un puntero a la funci on que especica el manejador, que es la funci on que se activa al recepcionar la se nal. La funci on debe devolver void y aceptar un argumento int. Si sa handler es SIG DFL se realizar a la acci on por defecto y si es SIG IGN se ignorar a. Durante la ejecuci on del manejador, la se nal que lo origin o permanece bloqueada junto al conjunto de se nales establecido en sa mask. El campo sa ags es una m ascara de bits que permite modicar el comportamiento del manejador, pudi endo construirse con una o m as de las siguientes propiedades: SA NOCLDSTOP. Si signum es SIGCHLD, no se recibe noticaci on cuando los procesos hijos se paren (esto es, cuando los procesos hijo reciben se nales SIGSTOP, SIGTSTP,SIGTTIN y SIGTTOUT). SA ONESHOT o SA RESETHAND. El manejador se ejecuta una vez y se restaura a continuaci on la acci on predeterminada. SA RESTART. Permite la reinicializaci on de llamadas de sistema. Se trata de un comportamiento compatible con la sem antica de se nales de BSD. SA NOMASK o SA NODEFER. No se impide que la se nal se reciba desde su propio manejador. SA SIGINFO. El manejador de se nal toma tres argumentos en lugar de uno. En este caso se debe congurar sa sigaction en lugar de sa handler. El campo sa sigaction se a nade a partir de la versi on 2.1.86 de Linux. Por lo tanto si se utiliza SA SIGINFO hay que usar el campo sa sigaction para establecer el manejador. La estructura siginfo t tiene el siguiente aspecto:
siginfo_t { int si_signo; //n umero de se~ nal int si_errno; //un valor errno int si_code; //c odigo de se~ nal

13

pid_t si_pid; //id del proceso emisor uid_t si_uid; // id del usuario real del proceso emisor int si_status; // valor de salida o se~ nal clock_t si_utime; //tiempo de usuario consumido clock_t si_stime; //tiempo de sistema consumido sigval si_value; //valor de se~ nal int si_int; // se~ nal POSIX-1b void *si_ptr; // se~ nal POSIX-1b void *si_addr; // direcci on de memoria que ha producido el fallo int si_band; // evento de conjunto int si_fd; // descriptor de fichero

La estructura contiene una serie de campos que se rellenan en funci on umero de se nal), si errno del tipo de se nal captada. Por ejemplo, si signo (n (c odigo de error) y si code (informaci on adicional sobre el origen de la se nal) se completan para todas las se nales. Si la se nal recibida es POSIX.1b y SIGCHLD los campos si pid y si uid son rellenados. La se nal SIGCHLD rellena si status, si utime y si stime. Las se nales POSIX.1b especican si int y si ptr. Las se nales SIGILL, SIGFPE, SIGSEGV y SIGBUS rellenan el campo on que provoc o el fallo. La se nal SIGPOLL utiliza los si addr, con la direcci campos si band y si fd. El valor si code permite obtener informaci on sobre el origen de la se nal mediante los datos de la siguiente tabla: SI USER: kill(), sigsend(), o raise(). SI KERNEL: El n ucleo. SI QUEUE: sigqueue(). SI TIMER: Finalizaci on de un temporizador POSIX. SI MESGQ: Cambio en el estado de la cola de mensajes POSIX. SI ASYNCIO: Finalizaci on de una entrada / salida as ncrona (AIO). SI SIGIO: SIGIO en cola. SI TKILL: tkill() o tgkill() (desde Linux 2.4.19) Si la se nal es SIGILL los valores de si code pueden ser: ILL ILLOPC: opcode ilegal. 14

ILL ILLOPN: operando ilegal. ILL ILLADR: modo de direccionamiento ilegal. ILL ILLTRP: trap ilegal ILL PRVOPC: opcode que requiere privilegios ILL PRVREG: registro que requiere privilegios ILL COPROC: error de coprocesador ILL BADSTK: error interno de la pila Si la se nal es SIGFPE, los valores de si code pueden ser: FPE INTDIV: divisi on entera por cero. FPE INTOVF: desbordamiento entero. FPE FLTDIV: divisi on de punto otante por cero. FPE FLTOVF: desbordamiento de punto otante. FPE FLTUND: underow de punto otante. FPE FLTRES: resultado inexacto de punto otante. FPE FLTINV: operaci on de punto otante inv alida FPE FLTSUB: subscript out of range Si la se nal es SIGSEGV, los valores de si code pueden ser: SEGV MAPERR: direcci on sin mapeo a objeto. SEGV ACCERR: permisos no v alido para objeto mapeado. Si la se nal es SIGBUS, los valores de si code pueden ser: BUS ADRALN: alineamiento inv alido. BUS ADRERR: direcci on f sica no existente. BUS OBJERR: error de hardware para objeto espec co. Si la se nal es SIGTRAP, los valores de si code pueden ser: 15

TRAP BRKPT: breakpoint en un proceso. TRAP TRACE: interceptaci on de la traza de un proceso. Si la se nal es SIGCHLD, los valores de si code pueden ser: CLD EXITED: se produjo la nalizaci on del proceso hijo. CLD KILLED: el proceso hijo fue terminado con kill. CLD DUMPED: terminaci on anormal del proceso hijo. CLD TRAPPED: un proceso hijo que est a siendo trazado ha sido interceptado. CLD STOPPED: detenci on del proceso hijo. CLD CONTINUED: un proceso hijo que ha sido previamente detenido continua (desde Linux 2.6.9). Si la se nal es SIGPOLL, los valores de si code pueden ser: POLL IN: datos de entrada disponibles POLL OUT: buers de salida disponibles POLL MSG: mensaje de entrada disponible POLL ERR: error de entrada / salida POLL PRI: entrada de alta prioridad disponible POLL HUP: dispositivo desconectado La llamada signal se us o desde las primeras versiones de linux. Hoy en d a ha sido sustituida por sigaction, pero en cualquier caso aqu damos su prototipo y una breve explicaci on de su funcionamiento.
void (*signal (int signum, void (*handler) (int))) (int);

El primer argumento es la se nal afectada. El segundo argumento es el puntero a una funci on de manejo de la se nal o bien alguna de las siguientes nal o SIG DFL para establecer otra constantes SIG IGN para ignorar la se vez el comportamiento predeterminado. La funci on de manejo de la se nal toma como argumento de entrada el n umero de la se nal que provoc o la desviaci on. La funci on signal devuelve un puntero a la funci on de desviaci on o la constante SIG ERR si se produce alg un error. 16

3.1.9.

Espera por una se nal.

Finalmente tenemos una funci on de espera:


int pause(void);

Esta funci on suspende el proceso que llama hasta la llegada de cualquier se nal al mismo. Devuelve siempre el valor -1 y en la funci on errno contiene el valor EINTR.

3.2.

Se nales m as utilizadas.

En esta secci on vamos a repasar las se nales m as comunes en un sistema Linux categorizadas por su funci on. Podemos utilizar las p aginas http://en.wikipedia.org/wiki/(nombre de la se nal) para encontrar m as informaci on sobre las distintas se nales. 3.2.1. Terminaci on del proceso.

SIGABRT. La se nal abort es utilizada por un programa normalmente para nalizar la ejecuci on de s mismo y producir un volcado del n ucleo para depuraci on. Para ello se suele utilizar la funci on abort() (ANSIC) que a su vez realiza un raise(SIGABRT) tambi en ANSI-C, que se implementa con kill(getpid(),SIGABRT). La funci on assert() realiza una llamada abort() cuando una aserci on falla. SIGQUIT. Se utiliza con el mismo prop osito de depuraci on que SIGABRT. La combinaci on de teclas Ctrl+"\", en una terminal provoca el env o por parte del n ucleo de esta se nal, que origina la nalizaci on del proceso y un volcado del n ucleo. SIGTERM. Esta se nal tiene el comportamiento por defecto de terminar el proceso inmediatamente. Sin embargo, los programadores normalmente realizan la interceptaci on de esta se nal para lograr una terminaci on correcta del programa. Es la se nal que env a por defecto kill. SIGKILL. Esta se nal sirve para terminar un proceso. Es una de las pocas se nales que no puede ser interceptada. El proceso es terminado inmediatamente salvo que est e ejecutando una fast system call, en cuyo caso habr a que esperar hasta la terminaci on de la llamada del sistema. SIGCHLD. Cuando los hijos de un proceso se eliminan, el padre recibe la se nal SIGCHLD. La interceptaci on de esta se nal puede ser u til, por ejemplo para hacer la llamada waitpid, s olo cuando es necesario. 17

3.2.2.

Para y reanuda un proceso.

SIGSTOP. Esta se nal lleva a un proceso a una estado de parada. Esta se nal no puede ser interceptada o ignorada. L ogicamente el proceso no puede autorecuperarse de esta llamada. Tiene que ser otro proceso el que mediante la se nal SIGCONT, le ordene continuar la ejecuci on. SIGCONT. Como comentamos, esta se nal se usa para reanudar un proceso que hab a sido detenido. Esta se nal es ignorada por los procesos que ya est an en ejecuci on. La se nal se puede interceptar, lo cu al es u ltil para que se realicen acciones espec cas antes de la reanudaci on. Si la se nal se ignora o se bloquea por un proceso que hubiera sido detenido, cuando recibe SIGCONT se reanuda inmediatamente. Es la interceptaci on de la se nal lo que se ignora o bloquea. SIGTSTP. Esta se nal se produce cuando se pulsa la combinaci on de teclas Ctrl-Z en un terminal (orden suspend). Es enviada al proceso en primer plano. Por defecto el proceso es detenido, pero esta se nal puede ser interceptada. Si ocurre esto u ltimo, el proceso no pasa a stop sino que se pasa a ejecutar la funci on de interceptaci on. Esta puede usarse para realizar acciones previas a la interceptaci on y nalizarse devolviendo a SIGTSTP su comportamiento por defecto y lanzar la se nal para suspenderlo de forma efectiva. 3.2.3. Se nales provocadas por fallo.

SIGFPE. Esta se nal se produce cuando un proceso realiza una operaci on matem atica ilegal, como por ejemplo, una divisi on por 0. SIGBUS. La se nal puede ocurrir por diversos motivos: errores de hardware, fallo en malloc() por falta de memoria, alineamiento incorrecto en determinadas arquitecturas. Aunque SIGBUS puede ignorarse o interceptarse, lo normal es mantener su comportamiento por defecto que es terminar con un volcado del n ucleo. SIGILL. Se produce ante la ejecuci on de una instrucci on ilegal. Podemos aplicar los mismos comentarios que para SIGBUS. SIGSEGV. Se env a a un proceso que trata de acceder a una zona de la memoria que no existe o para la cu al no tiene derechos de acceso. Un ejemplo es el intento de dereferenciar un puntero NULL. Por defecto SIGSEGV va a terminar el programa y realizar un volcado del n ucleo. 18

3.2.4.

Gesti on de terminales.

SIGTTOU. Cuando un proceso en segundo plano de un terminal trata de escribir en el terminal, puede recibir una se nal SIGTTOU. Esto se congura mediante la orden stty. Por defecto esta se nal naliza el proceso. SIGTTIN. Cuando un proceso en segundo plano de un terminal trata de leer del terminal, puede recibir una se nal SIGTTIN. Esto se congura mediante la orden stty. Por defecto, suspende la ejecuci on del proceso. SIGWINCH. Esta se nal es enviada a un proceso cuando la terminal donde se est a ejecutando cambia de tama no. El comportamiento por defecto es ignorar la se nal. Puede ser de utilidad, interceptar esta se nal por aquellos procesos que muestran informaci on en la terminal. SIGHUP. Esta se nal es generada por el kernel cuando se cierra una terminal de control. Los procesos que han sido lanzados desde esa terminal pertenecen al mismo grupo de procesos. Muchos procesos utilizan esta se nal para tomar consciencia del cierre de su teminal de control y releer sus cheros de conguraci on para actuar en consecuencia. Por ejemplo, para reinicializar el demonio inetd podemos hacer kill -HUP $inetd pid. Se puede hacer que un proceso ignore esta se nal si se ejecuta con el comando nohup. Por ejemplo: nohup wget www.ull.es &, lanza el proceso wget en segundo plano y se evita que al cerrar la terminal desde la que se lanz o se entregue la se nal SIGHUP al proceso, con lo que nalizar a. 3.2.5. Alarmas

SIGALRM. Esta se nal es enviada un tiempo despu es de que un proceso haga uso de la llamada del sistema alarm. El comportamiento por defecto de esta se nal es terminar con el programa.

3.3.

C odigos de ejemplo.

En esta secci on ilustraremos mediante c odigo la utlizaci on de diversas llamadas del sistema [Srinivasan, 2005]. El primer ejemplo muestra como se puede ignorar la se nal SIGINT en un proceso:
#include <stdio.h> #include <signal.h>

19

main() { signal(SIGINT,SIG_IGN); while(1) printf("El proceso ya no puede ser interrumpido con SIGINT\n"); return 0; }

Sin embargo, en muchas ocasiones es m as u til emplear un manejador para la se nal. Veamos el c odigo necesario para registrar uno:
struct sigaction mysig_act; mysig_act.sa_flags = SA_SIGINFO; mysig_act.sa_sigaction = (void *)mysig_handler; if(sigaction (<signal number>,&mysig_act,(struct sigaction *)NULL)) { printf("Sigaction returned error = %d\n", errno); exit(0); }

Como vemos, se est a utilizando la modalidad sigaction, por lo que sa ags est a asignada con SA SIGINFO. Cuando se usa esta modalidad, la funci on de interceptaci on es invocada con tres argumentos de entrada: el n umero de la se nal, la informaci on de la se nal y un volcado del contexto. Recordemos que la informaci on de la se nal se guarda en una estructura siginfo t que es rellenada por el kernel con informaci on sobre la generaci on de la se nal. La informaci on de contexto est a en la estructura ucontext que est a formada por punteros a otras estructuras como pt regs, mcontext t, sigset t, etc etera y contiene informaci on sobre los registros en el momento de producirse la se nal. La estructura pt regs, depende de la arquitectura y contiene los registros de prop osito general (GPRs), el puntero de instrucci on (EIP) y otros u tiles para la depuraci on. M as adelante veremos un ejemplo que hace uso de esta informaci on. El siguiente ejemplo muestra como interceptar la se nal SIGTERM y obtener informaci on a partir de la estructura siginfo t:
#include #include #include #include <stdio.h> <signal.h> <errno.h> <ucontext.h>

20

static void myhandler (unsigned int sn , siginfo_t *si , struct ucontext *sc) { unsigned int mnip; int i; printf(" n umero de la se~ nal = %d, signal errno = %d, signal code = %d\n", si->si_signo,si->si_errno,si->si_code); printf(" pid del emisor = %x, senders uid = %d, \n",si->si_pid,si->si_uid); } main() { struct sigaction s; s.sa_flags = SA_SIGINFO; s.sa_sigaction = (void *)myhandler; if(sigaction (SIGTERM,&s,(struct sigaction *)NULL)) { printf("Sigaction returned error = %d\n", errno); exit(0); } while(1); return 0; }

Veamos ahora una situaci on m as compleja como pueda ser la interceptaci on de una se nal SIGILL y el an alisis de la instrucci on ilegal, as como del contexto:
#include #include #include #include <stdio.h> <signal.h> <errno.h> <ucontext.h> si ,\

static void myhandler (unsigned int sn , siginfo_t struct ucontext *sc) { unsigned int mnip; int i,j;

printf(" Signal number = %d, Signal errno = %d\n" ,si.si_signo,si.si_errno); switch(si.si_code) { case 1: printf(" SI code = %d (Illegal opcode)\n",si.si_code);

21

break; case 2: printf(" SI code = %d break; case 3: printf(" SI code = %d si.si_code); break; case 4: printf(" SI code = %d break; case 5: printf(" SI code = %d break; case 6: printf(" SI code = %d break; case 7: printf(" SI code = %d break; case 8: printf(" SI code = %d break; default: printf("SI code = %d break; }

(Illegal operand)\n",si.si_code); (Illegal addressing mode)\n",

(Illegal trap)\n",si.si_code); (Privileged opcode)\n",si.si_code); (Privileged register)\n",si.si_code); (Coprocessor error)\n",si.si_code); (Internal stack error)\n",si.si_code); (Unknown SI Code)\n",si.si_code);

printf(" Machine State Register = %x \n", (((struct pt_regs *)((&(sc->uc_mcontext))->regs))->msr)); printf(" Link register pointing to location = 0x%x, \ Opcode at the location = 0x%x \n", (((struct pt_regs *)((&(sc->uc_mcontext))->regs))->link), *(unsigned int *) \ (((struct pt_regs *)((&(sc->uc_mcontext))->regs))->link)); for(i=20,j=5;i>0;i-=4,j--) printf(" Op-Code [nip - %d] = 0x%x at address = 0x%x \n" ,j,*(unsigned int *)(si.si_addr - i) ,(si.si_addr - i) ); printf(" Failed Op-code = 0x%x at address = 0x%x \n", *(unsigned int*)(si.si_addr), (si.si_addr)); printf(" Op-Code [nip + 1] = 0x%x at address = 0x%x \n", *(unsigned int *)(si.si_addr + 4), (si.si_addr + 4)); (((struct pt_regs *)((&(sc->uc_mcontext))->regs))->nip) += 4; } my() { __asm__ volatile ("add 4,5,6 \n\t":); __asm__ volatile ("add 7,8,9 \n\t":); __asm__ volatile ("mfmsr 3 \n\t":);

22

__asm__ volatile ("add 4,5,6 \n\t":); __asm__ volatile ("add 7,8,9 \n\t":); } main() { struct sigaction s; s.sa_flags = SA_SIGINFO; s.sa_sigaction = (void *)myhandler; if(sigaction (SIGILL,&s,(struct sigaction *)NULL)) { printf("Sigaction returned error = %d\n", errno); exit(0); } my(); return 0; }

En este ejemplo, se registra una funci on de captura para la se nal SIGILL y a continuaci on se ejecuta un c odigo que contiene una instrucci on privilegiada (habr a que pasar a modo kernel, para poder ejecutar la instrucci on). El manejador obtiene el c odigo que da informaci on m as precisa sobre el origen del fallo (si code) y utiliza la informaci on de contexto para mostrar el estado de algunos registros y el contenido de las direcciones previas a la que produjo el fallo. La salida del programa es:
> ./mysigill Signal number = 4, Signal errno = 0 SI code = 5 (Privileged opcode) Machine State Register = 4d032 Link register pointing to location = 0x10000830, Opcode at the location = 0x38000000 Op-Code [nip - 5] = 0x9421ffe0 at address = 0x10000788 Op-Code [nip - 4] = 0x93e1001c at address = 0x1000078c Op-Code [nip - 3] = 0x7c3f0b78 at address = 0x10000790 Op-Code [nip - 2] = 0x7c853214 at address = 0x10000794 Op-Code [nip - 1] = 0x7ce84a14 at address = 0x10000798 Failed Op-code = 0x7c6000a6 at address = 0x1000079c Op-Code [nip + 1] = 0x7c853214 at address = 0x100007a0

Como vemos est a informando de que se trata de un SIGILL provocado por un c odigo privilegiado. Adem as muestra que el opcode que provoca esto es 0x7c6000a6. Con el comando objdump podemos obtener informaci on del fuente del ejecutable. Esta es la salida donde se muestra efectivamente el opcode que da el problema: 23

10000788 <my>: 10000788: 1000078c: 10000790: 10000794: 10000798: 1000079c: 100007a0: 100007a4: 100007a8: 100007ac: 100007b0: 100007b4: 100007b8:

94 93 7c 7c 7c 7c 7c 7c 7c 81 83 7d 4e

21 e1 3f 85 e8 60 85 e8 03 61 eb 61 80

ff 00 0b 32 4a 00 32 4a 03 00 ff 5b 00

e0 1c 78 14 14 a6 14 14 78 00 fc 78 20

stwu stw mr add add mfmsr add add mr lwz lwz mr blr

r1,-32(r1) r31,28(r1) r31,r1 r4,r5,r6 r7,r8,r9 r3 <<< r4,r5,r6 r7,r8,r9 r3,r0 r11,0(r1) r31,-4(r11) r1,r11

El siguiente ejemplo, muestra la interceptaci on de la se nal SIGSEGV.


#include #include #include #include #include #include <stdio.h> <stdlib.h> <string.h> <signal.h> <errno.h> <ucontext.h> si , \

static void seghandler (unsigned int sn , siginfo_t struct ucontext *sc) { unsigned int mnip; int i;

mnip=*(unsigned int *)(((struct pt_regs *) \ ((&(sc->uc_mcontext))->regs))->nip); printf(" Signal number = %d, Signal errno = %d\n", si.si_signo,si.si_errno); switch(si.si_code) { case 1: printf(" SI code = %d (Address not mapped to object)\n", si.si_code); break; case 2: printf(" SI code = %d (Invalid permissions for \ mapped object)\n",si.si_code); break; default: printf("SI code = %d (Unknown SI Code)\n",si.si_code); break; } printf(" Intruction pointer = %x \n",mnip); printf(" Fault addr = 0x%x \n",si.si_addr); printf(" dar = 0x%x \n", (((struct pt_regs *)((&(sc->uc_mcontext))->regs))->dar)); printf(" trap = 0x%x \n",

24

(((struct pt_regs *)((&(sc->uc_mcontext))->regs))->trap)); printf(" Op-Code [nip - 4] = 0x%x at address = 0x%x \n", *(unsigned int *)\ (((struct pt_regs *)((&(sc->uc_mcontext))->regs))->nip-4), (((struct pt_regs *)((&(sc->uc_mcontext))->regs))->nip-4) ); printf(" Failed Op-code = 0x%x at address = 0x%x \n", *(unsigned int *)\ (((struct pt_regs *)((&(sc->uc_mcontext))->regs))->nip), (((struct pt_regs *)((&(sc->uc_mcontext))->regs))->nip)); printf(" Op-Code [nip + 1] = 0x%x at address = 0x%x \n", *(unsigned int *) \ (((struct pt_regs *)((&(sc->uc_mcontext))->regs))->nip+4), (((struct pt_regs *)((&(sc->uc_mcontext))->regs))->nip + 4)); printf("***GPR values are the time of fault*** \n"); for (i=0;i<11;i++) printf(" Gpr[%d] = 0x%x \n",i, \ (((struct pt_regs *)((&(sc->uc_mcontext))->regs))->gpr[i])); (((struct pt_regs *)((&(sc->uc_mcontext))->regs))->nip)+=4; } main() { struct sigaction m; char *p,*q, arr[]="Ma"; q=arr; m.sa_flags = SA_SIGINFO; m.sa_sigaction = (void *)seghandler; sigaction (SIGSEGV,&m,(struct sigaction *)NULL); *p++ = *q++; return 0; }

En este caso la salida es:


./sigsegv Signal number = 11, Signal errno = 0 SI code = 1 (Address not mapped to object) Intruction pointer = 98080000 Fault addr = 0x0 dar = 0x0 trap = 0x300 Op-Code [nip - 4] = 0x88090000 at address = 0x10000760 Failed Op-code = 0x98080000 at address = 0x10000764 Op-Code [nip + 1] = 0x396b0001 at address = 0x10000768 ***GPR values are the time of fault*** Gpr[0] = 0x4d Gpr[1] = 0xffffe070 Gpr[2] = 0x4001ee20 Gpr[3] = 0x0

25

Gpr[4] = 0xffffdf30 Gpr[5] = 0x0 Gpr[6] = 0xffffe110 Gpr[7] = 0xffffe114 Gpr[8] = 0x0 Gpr[9] = 0xffffe120 Gpr[10] = 0x0

El programa trata de copiar de una direcci on a otra, pero la direcci on de destino no ha sido reservada. El fallo es identicado como la referencia de una direcci on no mapeada a un objeto. Nos muestra la direcci on donde se produce el fallo y los registros de prop osito general. Si compilamos con la opci on -g tendremos en el chero ejecutable informaci on sobre el c odigo fuente, de manera que si usamos objdump obtenemos:
<Search output for the opcode "98080000"> 10000744: *p++ = *q++; 10000748: 1000074c: 10000750: 10000754: 10000758: 1000075c: 10000760: 10000764: 10000768: 1000076c: 10000770: 10000774: return 0; 10000778: } 48 01 07 21 38 81 38 81 7d 7d 88 98 39 91 39 91 df 46 ff 67 48 69 09 08 6b 67 4a 46 00 00 00 00 53 5b 00 00 00 00 00 00 a0 00 a4 00 78 78 00 00 01 00 01 00 bl addi lwz addi lwz mr mr lbz stb addi stw addi stw li 10010e64 <__bss_start+0x48> r6,r31,160 r10,0(r6) r7,r31,164 r11,0(r7) r8,r10 r9,r11 r0,0(r9) r0,0(r8) r11,r11,1 r11,0(r7) r10,r10,1 r10,0(r6) r0,0

<==Failed instruction

38 00 00 00

Como vemos, a partir de la direcci on del fallo observamos que el problema ocurre con la isntrucci on stbr 0, 0(r 8), pero si miramos el volcado de registros de prop ostio general vemos que el valor de r8 es 0, y de ah el fallo.

3.4.

Se nales en Posix threads.

Las se nales son introducidas en el ambito de los procesos. Llevar este concepto al ambito de los hilos no est a exento de dicultades y complicaciones, por el modelo de c omputo as ncrono que se introduce con las se nales [Butenhof, 1997]. Existen bastantes diferencias en el tratamiento de las se nales en el contexto de pthreads. Pensemos en primer lugar, en las se nales que son enviadas a los procesos, por ejemplo con el comando kill. Una vez la se nal llega al proceso, es distribuida al conjunto de hilos que lo componen. A partir de aqu nos encontramos con algunas asimetr as en diferentes aspectos del tratamiento de la se nal. Una se nal puede ser generada por la acci on de un thread invidual. Por ejemplo, si un thread intenta acceder a una parte de la memoria 26

para la que no tiene acceso o realiza una divisi on por cero, este thread individual ser a el que reciba la se nal. Un thread puede enviar una se nal a otro thread individual mediante la llamada pthread kill. En este caso, tambi en es u nicamente el thread de destino el que recibir a la se nal. Si la se nal es enviada al proceso, este la distribuye entre todos los threads que puedan manejarla, pero s olo uno se har a cargo de la misma. Si hay m as de uno en disposici on de recibirla, el thread de destino es elegido arbitrariamente. Si se desea que un thread concreto, sea el que reciba la se nal, habr a que bloquear el resto. Si todos los threads est an bloqueados, el primer thread que desbloquee la se nal, ser a el que la atienda. El manejador de la se nal es global a todo el proceso. Si se ha denido, ser a el que maneje la se nal independientemente del hilo al que se haya designado para recibir la se nal. El comportamiento de ignorar la se nal es tambi en global para todo el proceso. Es decir, si se decide ignorar la se nal, todos los threads lo van a hacer. Cualquier thread del proceso puede utilizar sigaction para establecer el manejador de la se nal o los comportamientos ignorar y establecer funcionamiento por defecto. Las llamadas al sistema normalmente usadas para el manejo de se nales con pthreads en Linux son:
#include <pthread.h> #include <signal.h> int pthread_sigmask(int how, const sigset_t mask); *newmask, sigset_t *old-

int pthread_kill(pthread_t thread, int signo); int sigwait(const sigset_t *set, int *sig);

La funci on pthread sigmask, permite el bloqueo de un conjunto de se nales en el thread que realiza la llamada. Los argumentos son similares a los de la funci on sigmask. La funci on pthread kill permite el env o de una se nal a 27

un thread particular. Por otra parte sigwait permite detener el thread que realiza la llamada hasta la recepci on de un conjunto de se nales particulares establecidos en set. Se recomienda revisar las notas sobre la utilizaci on de sigwait.

4.

Las se nales desde el punto de vista del kernel.

Vamos a dividir este resumen del modelo de se nales en el kernel 2.6 de Linux en dos partes: generaci on y entrega de la se nal. Para una completa descripci on, consultar [Bover y Cesati, 2005]

4.1.

Generaci on de la se nal

Cuando se desea enviar una se nal hacia un proceso, desde otro proceso o bien desde el propio kernel, se produce la denominada generaci on de la se nal. La generaci on se realiza por la ejecuci on de funciones del kernel: send sig(). Env a una se nal a un solo proceso. send sig info(). Env a una se nal a un solo proceso utilizando la estructura siginfo t. force sig(). Env a una se nal que no puede ser bloqueada o ignorada. force sig info(). Como la anterior pero usando siginfo t. force sig specic(). Esta funci on es del estilo force sig(), pero optimizada para las se nales SIGSTOP y SIGKILL. sys tkill(). Es la llamada del sistema asociada a tkill(). sys tgkill(). Es la llamada del sistema asociada a tgkill(). Existen funciones an alogas para el env o de se nales a grupos. Todas estas funciones suelen nalizar con una llamada a la funci on specic send sig info(). Esta funci on env a una se nal a un proceso y tiene tres argumentos: sig. El n umero de la se nal. info. O bien es un puntero a una estructura de datos de tipo siginfo t o contine alguno de los siguientes valores: 28

0. Se nal generada en user mode. 1. Se nal generada en kernel mode. 2. Se nal generada en kernel mode y es SIGSTOP o SIGKILL. t. Proceso de destino de la se nal. La llamada a specic send sig info() se realiza con interrupciones locales deshabilitadas y con el t->sighand->siglock adquirido, siendo t un puntero a un descriptor de proceso. La funci on specic send sig info() se encarga de realizar los siguientes pasos: 1. Comprueba si el proceso de destino debe ignorar la se nal. Si es as , retorna con un 0 (se nal no generada). Las condiciones para ignorar la se nal son 3 y se deben cumplir todas para decidir que hay que ignorar la se nal: 1. El proceso no est a en modo traza. Estos es, t->ptrace tiene PT TRACED=0. 2. La se nal no est a bloqueada. Es decir, sigismember(&t->blocked,sig) devuelve 0. la llamada

3. La se nal est a expl citamente ignorada, es decir el campo sa handler de t->sighand\_action[sig-1] es SIG IGN, o bien la se nal est a impl citamente ignorada, es decir el campo sa handler es SIG DFL y la se nal es SIGCONT, SIGCHLD, SIGURG o SIGWINCH. 2. Comprueba que la se nal no es de tiempo real (sig32) y que tengamos una ocurrencia de la misma en las se nales pendientes del proceso, para lo que hace la llamada sigismember(&t->pending,sig). Si la comprobaci on es armativa, es decir la se nal de no tiempo real est a ya como pendiente, el proceso de generaci on se interrumpe devolviendo un 0. 3. Ahora se invoca la funci on send\_signal(sig, info,t,&t->pending) para colocar la se nal en la estructura de datos del proceso que establece las se nales pendientes (t->pending). M as abajo veremos este paso en mayor detalle. 4. Si send signal termina exit osamente y la se nal no est a bloqueada, esto u ltimo se determina con sigismember(&t->blocked, sig), se invoca la funci on signal wake up() para noticar al proceso sobre la nueva se nal pendiente. Esta funci on realiza los siguientes pasos: 29

a. Coloca en t->thread\_info->flags TIF SIGPENDING.

la

bandera

b. Invoca la funci on try to wake up() para despertar el proceso si este est a en el estado TASK INTERRUMPIBLE o TASK STOPPED y la se nal es SIGKILL. c. Si try to wake up() devuelve 0, el proceso ya est a en estado RUNNABLE. Si el proceso est a en otra CPU env a una interrupci on a la otra CPU para forzar la replanicaci on del proceso. Cada vez que el proceso regresa de la funci on schedule(), se chequean las se nales pendientes del mismo, con lo cual nos aseguramos de que el proceso atienda las se nales pendientes. 5. Se decuelve el valor 1, que signica que la se nal fue generada con exito. Veamos ahora en mayor detalle el m etodo para colocar una se nal en la cola de se nales pendientes con la funci on send\_signal(sig,info,t,&t->pending). Nuevamente tenemos una secuencia de pasos: 1. Si info es 2 la se nal es o bien SIGKILL o SIGSTOP y ha sido generada por el kernel con force sig specic(). En este caso saltamos al paso 9. Como la acci on a realizar es inmediata y forzada por el kernel no es necesario a nadirla a la cola de se nales pendientes. 2. Si el n umero de se nales pendientes del propietario del proceso (t->user->sigpending) es menor que el l mite de recursos del proceso para las se nales pendientes (t->signal->rlim[RLIMIT\_SIGPENDING].rlim\_cur) la funci on ubica una estructura de datos del tipo sigqueue por la nueva ocurrencia de la se nal:
q=kmem_cache_calloc(sigqueue_cachep, GFP_ATOMIC)

3. Si el n umero de se nales pendientes para el propietario del proceso es demasiado alto, o la ubicaci on de memoria para la estructura sigqueue falla saltamos al paso 9. 4. Se incrementa el n umero de se nales pendientes para el usuario t->user->sigpending\end. 5. A nadimos la estructura sigqueue en la cola de se nales: 30

list_add_tail(&q->list,&signals->list)

6. Se rellena la tabla siginfo t dentro de la estructura sigqueue:


if((unsigned long) info == 0){ q->info.si_signo=sig; q->info.si_errno=0; q->info.si_code=SI_USER; q->info._sifields._kill._pid=current->pid; q->info._sifields._kill._uid=current->uid; }else if((unsigned long) info == 1){ q->info.si_signo=sig; q->info.si_code=SI_KERNEL; q->info._sifields._kill._pid=0; q->info._sifields._kill._uid=0; } else copy_siginfo(&q->info,info);

7. Coloca los bits de la m ascara de la cola de se nales.


sigaddset(&signals->signal,set);

8. Retorna con valor 0, pues la se nal ha sido exit osamente a nadida a la cola de se nales pendientes. 9. O hay demasiadas se nales pendientes o no hay memoria para otro elemento sigqueue o la se nal est a forzada por el kernel. Si la se nal es del tipo real-time hay que devolver un error si fue enviada para ser puesta en la cola:
if(sig>=32 && info && (unsigned long) info !=1 && info->si_code != SI_USER) return -EAGAIN;

10. Coloca el bit en la m ascara de la cola.


sigaddset(&signals->signal,sig);

11. Devuelve 0 incluso si la se nal no fue a nadida a la cola, pero el bit correspondiente en la m ascara de se nales pendientes fue puesto a 1. Como comentario general de esta secuencia de pasos, vemos que incluso si no hay memoria suciente para almacenar la estructura de datos de se nal pendiente en la cola correspondiente, el bit de la se nal se pone a 1 en la m ascara de se nales pendientes del proceso, para poder entregar la se nal. 31

4.2.

Entrega de la se nal.

La entrega (delivering) de la se nal puede que no se produzca inmediatamente despu es de la generaci on de la misma. Si el proceso no tiene la posesi on de la CPU en el momento en el que la se nal es generada, la entrega de la se nal se retrasar a. El kernel leer a la m ascara TIF SIGPENDING de un proceso cada vez que este vuelva a USER MODE. Por lo tanto, las se nales pendientes de un proceso se revisar an cada vez que el proceso vuelva de una interrupci on o una excepci on. La funci on que se encarga de gestionar la entrega de las se nales pendiente on recibe dos argumentos: es do signal(). Esta funci regs. Es la direcci on de la pila donde se guardan los contenidos del registro del USER MODE. oldset. En esta variable la funci on guardar a la m ascara de se nales bloqueadas. Tomar a el valor NULL si no hay necesidad de almacenar la m ascara. Veamos a grandes rasgos, el funcionamiento de do signal. Comienza con una comprobaci on para determinar si ha sido llamada desde una interrupci on. Si esto es as debe volver sin hacer nada, puesto que s olo deber a llamarse al volver al USER MODE:
if((regs->xcs & 3) !=3) return 1;

Si el par ametros oldset es enviado como NULL, la funci on do signal lo inicializa con la direcci on del campo bloqued del proceso actual current->bloqued:
if(!oldset) olset=&current->bloqued;

El funcionamiento de do signal() es el de un bucle que va llamando a la funci on dequeue signal(). Si esta funci on devuelve 0 es que ya no hay m as se nales pendientes que procesar y entonces do signal termina. La funci on dequeue signal() comienza por las se nales de menor n umero en la cola current->pending.signal. Luego contin ua con la cola current->signal->shared\_pending.signal. Su acci on es simple. Comienza poniendo a 0 el bit correspondiente en la m ascara de la cola privada o la cola compartida. Despu es llama a recalc sigpending() para actualizar el valor de TIF SIGPENDING. 32

De manera que dequeue signal() devuelve el n umero de se nal pendiente. A partir de ese momento do signal() se encarga de procesarla. Lo primero que hace es controlar si el proceso actual est a siendo monitorizado por alg un otro proceso. Si es as , llama a la funci on do notify parent cldstop() y a continuaci on realiza la llamada a schedule() para que el proceso que monitoriza pueda actuar en consecuencia de la se nal recibida por el proceso monitorizado. Ahora se carga la variable local ka con la estructura sigaction de la se nal a procesar.
ka=&current->sighand->action[signr-1];

Dependiendo del contenido, se podr a realizar alguna de las siguientes acciones: Ignorar la se nal. Ejecutar la acci on por defecto. Ejecutar la funci on de interceptaci on. Si la acci on es expl citamente ignorar la se nal, la funci on do signal() simplemente contin ua con la siguiente iteraci on del bucle:
if(ka->sa.sa_handler == SIG_IGN) continue;

Pasemos a ver ahora lo que ocurre cuando se trata de realizar la acci on por defecto, es decir:
ka->sa.sa_handler=SIG_DFL

La principal excepci on es el proceso init. Para este proceso se ignora la acci on por defecto, es decir, s mplemente:
if(current_pid==1) continue;

Las se nales cuya acci on por defecto es ignorar se tratan de manera simple:
if(signr==SIGCONT || signr== SIGCHLD || signr==SIGWINCH || signr== SIGURG) continue;

33

La se nal cuya acci on por defecto es STOP es un poco m as complicada. La acci on debe detener todos los procesos en el thread group. Para ello do signal debe establecer sus estados a TASK STOPPED y entonces invocar la funci on schedule(). Sin embargo, hay un matiz que puede apreciarse en el siguiente c odigo:
if(signr==SIGSTOP || signr==SIGTSTP || signr==SIGTTIN || signr==SIGTTOU){ if(signr!=SIGSTOP && is_orphaned_pgrp(current->signal->pgrp)) continue; do_signal_stop(signr); }

Como vemos a partir del c odigo, existe una diferencia entre SIGSTOP y el resto de se nales. SIGSTOP siempre detiene el thread group, mientras el resto de se nales detendr a el thread group s olo si no est a entre los process group hu erfanos. El est andar POSIX establece que un grupo de procesos no est a hu erfano mientras exista en el grupo un proceso que tenga un padre en otro grupo de procesos pero en la misma sesi on. Por lo tanto, si el proceso padre muere pero el usuario est a todav a en la sesi on, el grupo de procesos no se considera hu erfano. La funci on do signal stop() comprueba si current es el primer proceso a detener en el thread group. Si es as , activa un group stop. Esto consiste en que la funci on va a actualizar el contador group stop count en el descriptor de la se nal y a despertar cada uno de los procesos en el thread group. Al despertarse cada proceso va a mirar el campo group stop count y observando que tiene un valor positivo se da cuenta que hay un group stop en progreso. Entonces cambia su estado a TASK STOPPED e invoca schedule(). Finalmente, la funci on do signal stop() env a la se nal SIGCHLD al proceso padre del thread l der del grupo. Esto u ltimo no ocurrir a si en este proceso padre se ha activado la bandera SA NOCLDSTOP. Otra acci on por defecto es la terminaci on con un volcado de informaci on sobre el proceso (core dump). La se nales que tienen como acci on por defecto core dump vuelcan en el directorio de trabajo del proceso un chero con informaci on sobre el espacio de direcciones de memoria del proceso y los registros de la CPU. Una vez que do signal() ha volcado el chero, debe realizarse la nalizaci on del thread group. Esta nalizaci on es equivalente a la acci on terminate. Para ello se invoca la funci on do group exit() que realiza el procedimiento denominado group exit para terminar de forma limpia un thread group. La u tima posibilidad es la interceptaci on de la se nal por la utilizaci on de una funci on de interceptaci on o handler. Consiste en establecer una fun34

ci on que se ejecutar a como acci on en el proceso de entrega de la se nal. Este mecanismo es complejo, porque requiere la conmutaci on de modo kernel a modo usuario. La funci on do signal realiza lo siguiente:
handle_signal(signr, &info, &ka, oldset, regs); if(ka->sa.sa_flags & SA_ONESHOT) ka->sa.sa_handler=SIG_DFL; return 1;

Como vemos, si la se nal es entregada y tiene la bandera SA ONESHOT activada, s olo se llamar a la primera vez que sea entregada a la funci on de interceptaci on. Hay que tener en cuenta que los handlers son funciones denidas en modo usuario e incluidas dentro del segmento de c odigo del modo usuario. Sin ema desde bargo, handle signal() se ejecuta en modo kernel. Esto signica que ser el propio proceso desde donde se deba ejecutar la funci on de interceptaci on antes de seguir ejecutando su c odigo normalmente. Adem as, cuando el kernel trate de continuar ejecutando el proceso, la pila del kernel ya no contendr a el contexto hardware del programa interrumpido, pues esta pila se vac a en cada transici on de modo usuario a modo kernel. Para complicarlo a un m as, la funci on de interceptaci on podr a realizar una llamda al sistema. Al volver de la llamada al sistema hay que seguir ejecutando la funci on de interceptaci on, no el proceso. Vamos a describir en l neas generales como ser a el ujo de funciones que se realiza para lograr una interceptaci on de la se nal. Se env a la se nal no bloqueada. Cuando ocurre una interrupci on o excepci on, el proceso pasa a modo kernel. Antes de volver a modo usuario, se produce la entrega de la se nal con la ejecuci on de do signal(). Esta llama en este caso a handle signal(). La funci on handle signal() llama a setup frame() o setup frame rt(). El cometido de estas funciones no es otro que modicar la pila del modo usuario. Recordemos que la direcci on de retorno en una llamada a una funci on se guarda en la pila. Tambi en se modica el registro del puntero de instrucci on, para que al volver a modo usuario se empiece a ejecutar la funci on de interceptaci on. Se vuelve a modo usuario y se contin ua con la ejecuci on de la funci on de interceptaci on. Al terminar esta funci on y llegar a la instrucci on de retorno se va a ejecutar el c odigo incluido en la pila de modo usuario. 35

Este c odigo es una llamada del sistema denominada sigreturn(), cuya rutina de servicio en modo kernel realiza la copia del contexto de hardware del proceso norml nu evamente a la pila del modo kernel y restaura la pila del modo usuario en su situaci on original (funci on restore sigcontext()). As , al terminar esta llamada del sistema, el proceso puede reanudar su ejecuci on.

5.

Comandos de la bash shell relacionados con se nales.

En esta secci on revisaremos algunos comandos de la shell u tiles para la gesti on de las se nales [Tansley, 2000]. Como ya hemos mencionado el comando, que sirve para enviar se nales a procesos es kill. El formato es:
kill [-signal no:signal name] process ID

Veamos un ejemplo. Enviando la se nal 1 a un proceso, este releer a su chero de conguraci on. Por ejemplo, si estamos ejecutando un demonio de dns y modicamos el chero de conguraci on, no es necesario terminar el proceso y recomenzarlo. Basta enviarle una se nal SIGHUP que tiene el n umero 1. Veamos otro ejemplo. Queremos enviar la se nal SIGKILL a un proceso denominado mon web. Primero averiguamos cu al es el PID del proceso:
$ ps -ef | grep mon_web

A continuaci on llamamos a kill con el n umero obtenido:


$ kill -9 157

o bien,
$ kill SIGKILL 157

El comando trap permite interceptar se nales enviadas a nuestro proceso. El formato del comando trap es:
$ trap name signal(s)

36

El par ametro name es un conjunto de instrucciones a ser realizadas una vez la se nal es interceptada. El par ametro signal(s) son un conjunto de se nales. Veamos algunos ejemplos comunes:
trap "" 2 3

Las se nales 2 (SIGINT) y 3 (SIGQUIT) son interceptadas e ignoradas. El usuario no puede terminar el proceso.
trap "comandos" 2 3

En este caso cuando el usuario usa SIGINT o SIGQUIT para terminar, se ejecuta un conjunto de comandos.
trap 2 3

Las acciones de las se nales 2 y 3 son restauradas a sus valores por defecto. Ahora el usuario puede terminar el script. Con respecto a las utilidades de esta t ecnica podemos pensar en algunos ejemplos como borrar archivos temporales antes de terminar el proceso o bloquear la terminal, o ignorar un conjunto de se nales durante un proceso cr tico. Adem as de estos comandos hay que se nalar por su importancia el sistema de archivos proc, donde el kernel de Linux actualiza informaci on sobre el sistema. En dicho sistema de archivos, cada proceso tiene su propio directorio y dentro de el podemos encontrar el chero status. Este es un ejemplo de salida del comando: cat /proc/1044/status que muestra los atributos del proceso cuyo pid es 1044. Name: usb-storage-0 State: S (sleeping) Tgid: 1044 Pid: 1044 PPid: 1 TracerPid: 0 Uid: 0 0 0 0 Gid: 0 0 0 0 FDSize: 32 37

Groups: SigPnd: SigBlk: SigIgn: SigCgt: CapInh: CapPrm: CapEff:

0000000000000000 ffffffffffffffff 0000000000010000 0000000000000000 0000000000000000 00000000ffffffff 00000000fffffeff

Podemos ver el nombre de comando utilizado para arrancar el proceso, su estado (sleeping), sus identicadores (tgid, pid), el identicador del proceso padre, y m as abajo la m ascara de se nales pendientes (SigPnd), la m ascara de se nales bloqueadas (SigBlk), la m ascara de se nales ignoradas (SigIgn) y la correspondiente a las se nales capturadas (SigCgt).

6.

Bibliograf a

Referencias
[Bover y Cesati, 2005] Bover, D. y Cesati, M. (2005). Understanding de Linux Kernel, 3rd edition. OReilly. [Butenhof, 1997] Butenhof, D. (1997). Programming with POSIX Threads. Addison-Wesley. [Srinivasan, 2005] Srinivasan, M. (2005). Signals as a linux debugging tool. http://www-128.ibm.com/developerworks/linux/library/lsigdebug.html?ca=dgr-lnxw06SignalDebug. [Stallings, 2001] Stallings, W. (2001). Sistemas Operativos, cuarta edici o n. Prentice Hall. [Tansley, 2000] Tansley, D. (2000). Linux and Shell Programming. AddisonWesley. [Wall, 2001] Wall, K. (2001). Programaci on en Linux. Prentice-Hall.

38

Das könnte Ihnen auch gefallen