#include <routix/system.h>
#include <routix/task.h>
#include <signal.h>
#include <routix/signal.h>
#include <sys/types.h>
#include <routix/kstdio.h>
#include <error.h>
#include <routix/debug.h>
#include <routix/syscalls.h>
Go to the source code of this file.
|
Verifica si hay señales pendientes.
Definition at line 163 of file signal.c. Referenced by scheduler(), and sys_signal_check(). |
|
Ejecuta el handler por default para una señal.
Definition at line 297 of file signal.c. Referenced by exec_sigpending(). |
|
Prepara a la tarea para ejecutar un handler.
Definition at line 229 of file signal.c. Referenced by check_sigpending(). |
|
Definition at line 305 of file signal.c. Referenced by check_sigpending(), and exec_sig_default(). |
|
8-May-2004 Giro de 90º en la implementación base de señales. Por cuestiones de concurrencia, y un análisis "profundo" del impacto que tienen recibir una señal mientras se procesa un handler de otra, decidí sobre la marcha cambiar la implementación. Lo que haré será lo siguiente: El check_sigpending irá verificando si hay señales pendientes. Suponiendo que haya más de una, lo que se hará será lo siguiente. Para el primer handler se pondrá su dirección en el EIP del contexto de la tarea, y se pusheará al fondo del stack la dirección de un wrapper (creado por execve) el cuál me restaura los registros usados por la tarea, y luego hará un ret (para lo cual tuve que haber puesto el EIP del contexto al fondo del stack). Para las demás señales pendientes que haya detectado check_sigpending, se pusheará su dirección en el stack, con lo que primero se ejecutará la primera señal pendiente detectada, al finalizar está se iran sacando sistemáticamente del stack las direcciones de los handlers de la última a hacia la segunda pendiente. Es decir, si al momento de schedulear tengo pendientes las señales 1,2,3 y 10 primero se ejecutará la 1, luego la 10, la 3 y finalmente la 2. 9-May-2004 La gata flora se apodera de mi :-) Luego de haber implementado el sistema comentado anteriormente, me he dado cuenta que no funciona (al menos no como lo implementé). Cuál es el problema ? Todos los handlers logran ejecutarse con continuidad sin que el kernel se entere. Si bien esto es altamente performante :-), no me permite ir variando la máscara de señales ( task->sigmask ) la cual me dice que señales debo ignorar mientras estoy en el handler actual. Asi que, anulo esta implementación y vuelvo a algo muy cercano a la implementación original. En primer lugar: la función check_sigpending recibe como parámetro un flag, el cuál indica si debe o no guardar el contexto de la tarea. Esto creo lo explique en reiteradas oportunidades, pero en pos de aumentar la posibilidad de comprensión, lo volveré a explicar. Sólo debo guardar el contexto de la tarea en dos ocasiones: 1) Si tengo que interrumpir la ejecución de la tarea para ejecutar un handler. 2) Si debo interrumpir la interrupción de un handler para ejecutar otro handler (siempre y cuando task->sigmask me lo permita). En que caso no debo guardar el contexto ? Supongamos que estaba ejecutando la tarea, y recibo dos señales: guardo el contexto y ejecuto un handler. Cuando retorno de ejecutar ese handler (vía INT sys_signal_check) puedo ejecutar el otro handler sin necesidad de guardar el contexto, lo cuál hice unos instantes antes. A continuación se detalla el wrapper que se ejcuta una vez terminado el handler (el cuál se carga en la llamada execve): *(ptr_exit+12) = 0xb8; // Codigo de operacion: "mov eax, " *(unsigned long *)(ptr_exit+13) = SYS_SIGNALS | SYS_SIGNAL_CHECK; *(ptr_exit+17) = 0xcd; // int *(ptr_exit+18) = 0x50; // 0x50 *(ptr_exit+19) = 0x5e; // pop esi // Con estos popeos recupero los registros de propósito general *(ptr_exit+20) = 0x5f; // pop edi // que fueron pusheados antes de ejecutar un handler de señal *(ptr_exit+21) = 0x5a; // pop edx *(ptr_exit+22) = 0x59; // pop ecx *(ptr_exit+23) = 0x5b; // pop ebx *(ptr_exit+24) = 0x58; // pop eax *(ptr_exit+25) = 0xc3; // retn new_task->sigcheck_addr = GET_OFFSET(ptr_exit + 12) + mem->vdir; Este wrapper se ubica en el segmento de datos de la tarea, y se guarda su dirección en task->sigcheck_addr. Esa dirección se pone en el stack de la tarea, como dirección de retorno del handler :-) 12-May-2004 Ya tenemos la base del sistema de posteo y entrega (post & deliver) de señales. Es el momento de hablar de la implementación de las máscaras de señal. Vamos a tener dos tipos de máscaras: la primera seteada mediante la llamada al sistema sigprocmask, la cuál indica a que señales debemos inhibir mientras ejecutamos código de la tarea; mientras que el segundo tipo será la máscara definida mediante la llamada sigaction la cuál nos dice que señales debo "ignorar" mientras ejecuto el handler de una señal en particular. Vale aclarar que el término "ignorar" no es del todo correcto, ya que según POSIX, cuando se IGNORA una señal, se la descarta (definiendo como handler de la señal a SIG_IGN). Que pasa entonces cuando yo pongo una máscara de señal que inhiba a la señal SIGCHLD, por ejemplo ? Cuando se reciba esa señal, se la va a marcar como pendiente (post), pero su handler se ejecutará (deliver) recién cuando se "desinhiba" de la máscara. A continuación voy a intentar dar una idea de como será la implementación. La forma real de entenderla es estudiando el código (y sí, no hay otra). Se ejecuta check_sigpending, para ver si hay señales pendientes, cuando se encuentra alguna, antes de preparar todo para ejecutar el handler, verifico si esa señal está inhibida mediante la máscara sigmask (que se encuentra en el task_struct de la tarea), usando la macro IS_MASKED. Obviamente la señal no se ejecuta mientras se encuentre inhibida. Ese valor sigmask, de donde sale ? Como ya aclaré anteriormente, puede ser seteado con la llamada sigprocmask o bien, es el valor sa_mask de la estructura sigaction, la cual se se define mediante la llamada sigaction. En esta primera aproximación, haremos lo siguiente. Cuando vamos a ejecutar un handler, pondremos como máscara (es decir en sigmask) al sa_mask de ese handler, y resguardaremos el valor que poseía sigmask en el mismo stack que vinimos manipulando (Si si, otra vez a meter mando en el stack...). Cuando se termine de ejecutar el handler, popearemos el valor del stack y lo regresaremos a sigmask. Para lograr esto tuvimos que modificar el código de sys_signal_check (sys_signal.c) y el de exec_sigpending (signal.c). Sencillamente pusheamos el valor de la máscara actual antes de pushear "signo" (el cuál es el parámetro que recibe el handler de señal). Luego, al retornar de la ejecución del handler, simplemente se popea la máscara. Realmente no mire como implementaron esto otros desarrolladores, pero me imagino que debe ser de una manera terriblemente similar. Me imagino esto ya que, al realizar testeos, encuentro que si utilizo sigprocmask desde un handler de señal, al salir del handler, esa nueva máscara que coloque la pierdo, es decir, se sobreescribe con la que se recupera del stack. Al ir a POSIX (particularmente sigprocmask) encuentro algo como esto: "When a thread's signal mask is changed in a signal-catching function that is installed by sigaction() the restoration of the signal mask on return from the signal-catching function overrides that change (see sigaction()).", lo que me indica que probablemente hallan almacenado las máscaras dentro de una pila. 22-May-2004 Es realmente problematico que no se encolen las señales, ya que si un proceso crea 10 hijos, y todos ellos terminan simultaneamente, varios quedarán en Zombies, ya que hay solo 1 bit disponible en task->sigpending. Se me ocurrió como forma fácil de solucionar esto, colocar en la estructura task_signal un entero por cada señal, en el cuál podemos almacenar la cantidad de señales de un tipo que hay. Cada vez que ejecutamos un handler, decrementamos ese valor. De este modo, no perdemos señales. Definition at line 153 of file signal.c. Referenced by sys_show(). |
|
Definition at line 154 of file signal.c. Referenced by sys_show(). |