senales
Diferencias
Muestra las diferencias entre dos versiones de la página.
| Próxima revisión | Revisión previa | ||
| senales [2012/09/24 23:32] – creado lmateu | senales [2016/11/24 13:13] (actual) – [sigaction/sigprocmask] lmateu | ||
|---|---|---|---|
| Línea 72: | Línea 72: | ||
| Ejemplo: Lectura con timeout. | Ejemplo: Lectura con timeout. | ||
| + | |||
| + | < | ||
| + | /* Leer una línea del teclado dándole 10 segundos de plazo */ | ||
| + | #include < | ||
| + | #undef __USE_BSD /* para que el read pueda ser interrumpido por una señal */ | ||
| + | #include < | ||
| + | #include < | ||
| + | | ||
| + | volatile int flag; | ||
| + | | ||
| + | /* función para atrapar la señal de alarma */ | ||
| + | void ring() { | ||
| + | flag=0; | ||
| + | } | ||
| + | | ||
| + | /* función que lee con timeout */ | ||
| + | int gettext(char *buf, int bufsize, int timeout) { | ||
| + | int nchars; | ||
| + | | ||
| + | signal(SIGALRM, | ||
| + | flag= 1; | ||
| + | alarm(timeout); | ||
| + | nchars= read(STDIN_FILENO, | ||
| + | alarm(0); /* para cancelar alarma pendiente */ | ||
| + | if (!flag) | ||
| + | nchars= 0; | ||
| + | buf[nchars]= ' | ||
| + | return nchars; | ||
| + | } | ||
| + | | ||
| + | #define MAXLINEA 100 | ||
| + | int main() { | ||
| + | char linea[MAXLINEA+1]; | ||
| + | | ||
| + | printf(" | ||
| + | fflush(stdout); | ||
| + | if (gettext(linea, | ||
| + | printf(" | ||
| + | else | ||
| + | printf(" | ||
| + | return 0; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== Señales y setjmp/ | ||
| + | |||
| + | Supongamos que queremos llamar una función, pero si la función toma más tiempo que digamos 2 segungos queremos | ||
| + | abortar la llamada. | ||
| + | longjmp desde la rutina de atención de la señal: | ||
| + | |||
| + | < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | #include < | ||
| + | |||
| + | /* | ||
| + | * Ejemplo de uso de signal y longjmp para implementar un timeout | ||
| + | * Esta implementacion usualmente solo funciona para la primera invocacion | ||
| + | */ | ||
| + | |||
| + | static jmp_buf ring; | ||
| + | |||
| + | void clock(int sig) | ||
| + | { | ||
| + | longjmp(ring, | ||
| + | } | ||
| + | |||
| + | int call_with_timeout(int (*f)(void *), void *p, int timeout) | ||
| + | { | ||
| + | int res; | ||
| + | void (*hdlr)(); | ||
| + | |||
| + | hdlr = signal(SIGALRM, | ||
| + | |||
| + | if (setjmp(ring) != 0) { | ||
| + | signal(SIGALRM, | ||
| + | fprintf(stderr, | ||
| + | return 1; | ||
| + | } | ||
| + | |||
| + | alarm(timeout); | ||
| + | res = f(p); | ||
| + | printf(" | ||
| + | alarm(0); | ||
| + | |||
| + | signal(SIGALRM, | ||
| + | return(res); | ||
| + | } | ||
| + | |||
| + | int fun(void *p) { | ||
| + | sleep(2); | ||
| + | return 0; | ||
| + | } | ||
| + | |||
| + | int main() { | ||
| + | call_with_timeout(fun, | ||
| + | call_with_timeout(fun, | ||
| + | return 0; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Este código funciona en Linux, pero debido a un problema de estandarización, | ||
| + | Unix. El problema es que en algunos Unix la rutina de atención del timer (ejemplo: clock) se llama con | ||
| + | la señal SIGALRM deshabilitada. | ||
| + | señal queda deshabilitada y por lo tanto la señal de un segundo timeout podría nunca recibirse. | ||
| + | |||
| + | Este es el caso de Linux, pero afortunadamente hay otro aspecto que evita el problema el Linux. | ||
| + | setjmp graba el estado de las señales que están activas/ | ||
| + | En buenas cuentas longjmp activa nuevamente la señal SIGALRM y por eso funciona. | ||
| + | estándar en todos los Unix. Sin embargo sí es estándar que las funciones sigsetjmp y siglongjmp graban | ||
| + | el estado de las señales o no de acuerdo a un parámetro de sigsetjmp. | ||
| + | estas funciones en el código de más arriba: | ||
| + | |||
| + | < | ||
| + | #define _POSIX_C_SOURCE 1 | ||
| + | |||
| + | void clock(int sig) { | ||
| + | siglongjmp(ring, | ||
| + | } | ||
| + | | ||
| + | ... | ||
| + | | ||
| + | int call_with_timeout(int (*f)(void *), void *p, int timeout) { | ||
| + | ... | ||
| + | if (sigsetjmp(ring, | ||
| + | ... | ||
| + | } | ||
| + | ... | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Es necesario definir la macro _POSIX_C_SOURCE porque no es una función estándar de ansi-C. | ||
| + | POSIX sí la define. | ||
| + | |||
| + | === Discusión === | ||
| + | |||
| + | Otro punto importante que no se debe olvidar es lo que pasa con los recursos solicitados por la | ||
| + | función. | ||
| + | jamás y por lo tanto se transforman en goteras. | ||
| + | una traza de todos los recursos pedidos para así liberarlos cuando se cumpla el timeout. | ||
| + | no es trivial de hacer. | ||
| + | |||
| + | También hay que considerar que la invocación de la rutina que atiende la señal puede provocar dataraces. | ||
| + | Por ejemplo supongamos que la función f usa malloc. | ||
| + | del malloc y por lo tanto el heap que maneja malloc queda en un estado inconsistente. | ||
| + | la señal también invoca malloc, se puede producir una inconsistencia que gatille un segmentation fault o | ||
| + | que se entregue 2 veces el mismo pedazo de memoria. | ||
| + | señal deben ser simples como asignar una variable global por ejemplo. | ||
| + | ==== sigaction/ | ||
| + | |||
| + | La función signal es la primera función que existió en Unix para atrapar señales. | ||
| + | usar la función sigaction para tener más control sobre donde y cómo se debe ejecutar la rutina de atención. | ||
| + | Por ejemplo: | ||
| + | |||
| + | * si la señal se deshabilita o no mientras se ejecuta la rutina de atención | ||
| + | * en que pila se ejecuta la señal | ||
| + | * si la señal vuelve a su estado por omisión una vez que se gatilla la señal | ||
| + | * etc. | ||
| + | |||
| + | Del mismo modo, sigprocmask permite deshabilitar/ | ||
| + | para resolver el datarace asociado al uso de malloc tanto en el código en donde puede ocurrir una señal como en la función | ||
| + | que atiende esa señal. | ||
| + | invocar nuevamente sigprocmask para reactivar nuevamente la señal. | ||
| + | malloc, la señal quedará pendiente y solo se invocará la rutina de atención cuando se reactive la señal en la | ||
| + | segunda llamada de sigprocmask. | ||
| + | |||
| + | Consulte la página del manual para averiguar más sobre estas funciones. | ||
| + | |||
| + | ==== Ejercicio ==== | ||
| + | |||
| + | Resuelva la parte c de la pregunta 1 del [[http:// | ||
senales.1348529575.txt.gz · Última modificación: por lmateu
