2012-05-23 14 views
8

En el siguiente programa, si elimino el comentario de la línea _XOPEN_SOURCE, mi programa termina cuando toco C-c, el mismo programa no finaliza si no comento esa línea. ¿Alguien sabe de qué forma afecta _XOPEN_SOURCE el manejo de la señal? Estoy en Linux con gcc (4.6.3) y glibc (2.15).XOPEN_SOURCE y gestión de señal

/* #define _XOPEN_SOURCE 700 */ 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <signal.h> 

typedef void (*sighandler_t)(int); 

void handle_signal(int signo) 
{ 
    printf("\n[MY_SHELL] "); 
    fflush(stdout); 
} 

int main() 
{ 
    int c; 
    signal(SIGINT, SIG_IGN); 
    signal(SIGINT, handle_signal); 
    printf("[MY_SHELL] "); 
    while ((c = getchar()) != EOF) { 
     if (c == '\n') 
      printf("[MY_SHELL] "); 
    } 
    printf("\n"); 
    return 0; 
} 
+0

Como un aparte, 'char c': no, no, no, no, no. 'EOF' necesita estar fuera de banda. Es por eso que 'getchar' devuelve un' int'. – Dave

+0

@Dave Tienes razón. En realidad, fue un ejemplo que encontré en internet. No lo noté Arreglando eso ahora. – yasar

Respuesta

4

El problema es que la función signal() puede tener dos formas diferentes de comportamiento cuando la instalación de una función de tratamiento de señal:

  • semántica Sistema V, donde el manejador de señal es "one-shot" - que es, después de que se llama a la función de manejo de señal, la disposición de la señal se restablece a SIG_DFL - y las llamadas al sistema que son interrumpidas por la señal no se reinician; o
  • Semántica de BSD, donde el manejador de señal es no reinicie cuando la señal se dispara, la señal se bloquea mientras el manejador de señal se está ejecutando, y la mayoría de las llamadas interrumpidas del sistema se reinician automáticamente.

En Linux con glibc, se obtiene la semántica BSD si se define _BSD_SOURCE, y la semántica System V si no lo está. La macro _BSD_SOURCE se define de manera predeterminada, pero esta definición predeterminada se suprime si define _XOPEN_SOURCE (o algunas otras macros también, como _POSIX_SOURCE y _SVID_SOURCE).

semántica Bajo System V, si la llamada read() sistema subyacente getchar() es interrumpido por SIGINT continuación getchar() volverán EOF con errno conjunto de EINTR (esto hará que su programa para salir normalmente). Además, después de la primera SIGINT, la disposición de esta señal se restablece al valor predeterminado, y la acción predeterminada para SIGINT es finalizar el proceso (por lo que incluso si su programa sobrevivió al primer SIGINT, el segundo provocaría que salga anormalmente).

La solución no es usar signal() en absoluto para instalar funciones de manejo de señal; en su lugar, debe usar sigaction(), que es portátil; brinda la misma semántica en todas partes. Con sa_flags establecido en SA_RESTART, sigaction() dará la semántica BSD, que es lo que desea.

+0

Pero debo tener en cuenta que, en cualquier caso, el programa no terminará cuando se presione C-c por primera vez (con el controlador ya configurado). –

+0

Eso no es lo que está pasando aquí. La semántica '_XOPEN_SOURCE 700' no tiene' SA_RESTART', lo que causa 'EINTR', y devuelve' EOF' en getchar, saliendo del programa. Ver mi respuesta – Dave

+0

@Dave: Correcto, he actualizado esta respuesta con la información de reinicio de syscall. – caf

1

Estas diferencias behavorial sutiles son por qué sigprocmask() habitualmente se prefiere signal() La versión con _XOPEN_SOURCE 700 llamadas definidas (como se muestra por strace)

rt_sigaction(SIGINT, {SIG_IGN, [], SA_INTERRUPT|SA_NODEFER|SA_RESETHAND}, {SIG_DFL, [], 0}, 8) = 0 
rt_sigaction(SIGINT, {0x80484dc, [], SA_INTERRUPT|SA_NODEFER|SA_RESETHAND}, {SIG_IGN, [], SA_INTERRUPT|SA_NODEFER|SA_RESETHAND}, 8) = 0 

Considerando que el de salida comentado uno llama:

rt_sigaction(SIGINT, {SIG_IGN, [INT], SA_RESTART}, {SIG_DFL, [], 0}, 8) = 0 
rt_sigaction(SIGINT, {0x80484dc, [INT], SA_RESTART}, {SIG_IGN, [INT], SA_RESTART}, 8) = 0 

La bandera adicional importante que falta en _XOPEN_SOURCE es SA_RESTART que permite que la llamada al sistema read continúe como si no hubiera señal. Sin eso, la llamada al sistema indica falla, getchar() devuelve -1 (pero indica un error, en lugar de un verdadero EOF), y su programa finaliza.