2012-05-23 19 views
8

Nel seguente programma, se si decommenta la riga _XOPEN_SOURCE, il mio programma termina quando si preme C-c, lo stesso programma non termina Se non commento quella riga. Qualcuno sa in che modo _XOPEN_SOURCE influisce sulla gestione del segnale? Sono su Linux con gcc (4.6.3) e glibc (2.15).XOPEN_SOURCE e gestione dei segnali

/* #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

A parte, 'char c': no, no, no, no, no. 'EOF' deve essere fuori banda. Ecco perché 'getchar' restituisce un' int'. – Dave

+0

@Dave Hai ragione. In realtà è stato un esempio che ho trovato su internet. Non l'ho notato. Correggendolo adesso. – yasar

risposta

4

Il problema è che la funzione signal() può avere due diverse forme di comportamento durante l'installazione di una funzione di gestione del segnale:

  • semantica System V, in cui il conduttore di segnale è "one-shot" - che dopo aver richiamato la funzione di gestione del segnale, la disposizione del segnale viene reimpostata su SIG_DFL e le chiamate di sistema interrotte dal segnale non vengono riavviate; o
  • semantica BSD, in cui il gestore di segnale è non ripristinato quando il segnale si attiva, il segnale viene bloccato mentre il gestore di segnale è in esecuzione e la maggior parte delle chiamate di sistema interrotte vengono automaticamente riavviate.

su Linux con glibc, si ottiene la semantica BSD se _BSD_SOURCE è definito, e la semantica System V, se non lo è. La macro _BSD_SOURCE è definita per impostazione predefinita, ma questa definizione predefinita è soppressa se si definisce _XOPEN_SOURCE (o anche alcuni altri macro, come _POSIX_SOURCE e _SVID_SOURCE).

semantica System V, se la chiamata di sistema read() sottostante getchar() è interrotta da SIGINT poi getchar() torneranno EOF con errno set di EINTR (ciò impedirebbe la programma per uscire normalmente). Inoltre, dopo il primo SIGINT, la disposizione di questo segnale viene ripristinata al valore predefinito e l'azione predefinita per SIGINT termina il processo (quindi, anche se il programma è sopravvissuto al primo SIGINT, il secondo causerebbe l'uscita anomala).

La soluzione non deve utilizzare signal() per l'installazione di funzioni di gestione dei segnali; invece, dovresti usare sigaction(), che è portatile - dà la stessa semantica ovunque. Con sa_flags impostato su SA_RESTART, sigaction() darà la semantica BSD, che è ciò che desideri.

+0

Ma dovrei notare che in ogni caso il programma non termina quando C-c viene premuto la prima volta (con il gestore già impostato). –

+0

Questo non è quello che sta succedendo qui. La semantica '_XOPEN_SOURCE 700' manca' SA_RESTART', causando 'EINTR', e restituendo' EOF' in getchar, uscendo dal programma. Vedi la mia risposta. – Dave

+0

@Dave: hai ragione, ho aggiornato questa risposta con le informazioni di riavvio di syscall. – caf

1

Queste differenze Behavorial sottili sono perché sigprocmask() è generalmente preferibile signal() La versione con _XOPEN_SOURCE 700 chiamate definite (come mostrato dalla 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 il commentata-out si chiama:

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 

L'importante flag in più che _XOPEN_SOURCE manca è SA_RESTART che consente alla chiamata di sistema read di continuare come se non si fosse verificato alcun segnale. In caso contrario, la chiamata di sistema indica un errore, getchar() restituisce -1 (ma indica un errore, anziché un EOF vero) e il programma termina.