2010-11-18 15 views
109

Come si cattura Ctrl + C in C?Catch Ctrl-C in C

+5

Non c'è niente come segnali in C .... o almeno così ho pensato fino a quando ho letto lo standard C99. Si scopre che esiste una gestione del segnale definita in C, ma Ctrl-C non è obbligato a produrre alcun segnale specifico o alcun segnale. A seconda della piattaforma, questo potrebbe essere impossibile. – JeremyP

+1

La gestione del segnale dipende principalmente dall'implementazione. Su piattaforme * nix, utilizza e, se sei su OSX, puoi sfruttare GCD per rendere le cose ancora più facili ~. –

risposta

152

Con un gestore di segnale.

Ecco un semplice esempio lanciando una bool utilizzato in main():

#include <signal.h> 

static volatile int keepRunning = 1; 

void intHandler(int dummy) { 
    keepRunning = 0; 
} 

// ... 

int main(void) { 

    signal(SIGINT, intHandler); 

    while (keepRunning) { 
     // ... 

Modifica nel giugno 2017: Per GSA può interessare, in particolare quelli con una voglia insaziabile di modificare questa risposta. Guarda, ho scritto questa risposta sette anni anni fa. Sì, cambiano gli standard linguistici. Se davvero devi migliorare il mondo, per favore aggiungi la tua nuova risposta ma lascia il mio così com'è. Poiché la risposta ha il mio nome, preferirei che contenga anche le mie parole. Grazie.

+2

http://www.google.com/#q=c+signal+handler – sbi

+0

Abbastanza corretto ma la domanda era su Ctrl-C e risponde a questo - quindi non c'è bisogno di downvotare. –

+0

Ok, abbastanza giusto. –

39

Controllare qui:

Nota: Ovviamente, questo è un semplice esempio che spiega solo come impostare un CtrlC gestore, ma come sempre ci sono regole che devono essere rispettate per non rompere qualcos'altro. Si prega di leggere i commenti qui sotto.

Il codice di esempio dall'alto:

#include <stdio.h> 
#include <signal.h> 
#include <stdlib.h> 

void  INThandler(int); 

int main(void) 
{ 
    signal(SIGINT, INThandler); 
    while (1) 
      pause(); 
    return 0; 
} 

void INThandler(int sig) 
{ 
    char c; 

    signal(sig, SIG_IGN); 
    printf("OUCH, did you hit Ctrl-C?\n" 
      "Do you really want to quit? [y/n] "); 
    c = getchar(); 
    if (c == 'y' || c == 'Y') 
      exit(0); 
    else 
      signal(SIGINT, INThandler); 
    getchar(); // Get new line character 
} 
+18

Non esiste una cosa come 'void main' ... –

+2

@Derrick Agree,' int main' è una cosa appropriata, ma 'gcc' e altri compilatori lo compilano con un programma in esecuzione corretto sin dagli anni '90. Spiegato abbastanza bene qui: http://www.eskimo.com/~scs/readings/voidmain.960823.html - è fondamentalmente una "caratteristica", lo prendo così. –

+1

@ icyrock.com: Tutto molto vero (per quanto riguarda void main() in C), ma quando pubblichi pubblicamente è probabilmente meglio evitare del tutto il dibattito usando int main() per non distrarlo dal punto principale. – Clifford

10

Impostare una trappola (si può intrappolare diversi segnali con un gestore):

 
signal (SIGQUIT, my_handler); 
signal (SIGINT, my_handler); 

gestire il segnale come vuoi, ma essere consapevoli di limitazioni e trucchi:

 
void my_handler (int sig) 
{ 
    /* Your code here. */ 
} 
8

Oppure si può mettere il terminale in modalità raw, come questo:

struct termios term; 

term.c_iflag |= IGNBRK; 
term.c_iflag &= ~(INLCR | ICRNL | IXON | IXOFF); 
term.c_lflag &= ~(ICANON | ECHO | ECHOK | ECHOE | ECHONL | ISIG | IEXTEN); 
term.c_cc[VMIN] = 1; 
term.c_cc[VTIME] = 0; 
tcsetattr(fileno(stdin), TCSANOW, &term); 

Ora dovrebbe essere possibile leggere Ctrl +C sequenze di tasti utilizzando fgetc(stdin). Attenzione, però, perché con questo non si può Ctrl +Z, Ctrl +Q, Ctrl +S, ecc come normalmente più neanche.

3

Per quanto riguarda le risposte esistenti, tenere presente che la gestione del segnale dipende dalla piattaforma. Ad esempio, Win32 gestisce molti meno segnali rispetto ai sistemi operativi POSIX; see here. Mentre SIGINT è dichiarato in signals.h su Win32, consultare la nota nella documentazione che spiega che non farà ciò che ci si potrebbe aspettare.

2

Questo stampa appena prima di uscire.

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 

void sigint_handler(int); 

int main(void) 
{ 
    signal(SIGINT, sigint_handler); 

    while (1){ 
     pause(); 
    }   
    return 0; 
} 

void sigint_handler(int sig) 
{ 
    /*do something*/ 
    printf("killing process %d\n",getpid()); 
    exit(0); 
} 
21

Addendum relativo alle piattaforme UN * X.

Secondo la pagina signal(2) uomo su GNU/Linux, il comportamento di signal non è portatile come il comportamento di sigaction:

Il comportamento del segnale() varia tra le versioni di UNIX, e ha anche varia storicamente attraverso diverse versioni di Linux. Evita l'uso di : usa invece sigaction (2).

Su Sistema V, il sistema non ha bloccato la consegna di ulteriori istanze del segnale e l'invio di un segnale reimposta il gestore su quello predefinito. In BSD la semantica è cambiata.

la seguente variazione della risposta precedente Dirk Eddelbuettel usa sigaction anziché signal:

#include <signal.h> 
#include <stdlib.h> 

static bool keepRunning = true; 

void intHandler(int) { 
    keepRunning = false; 
} 

int main(int argc, char *argv[]) { 
    struct sigaction act; 
    act.sa_handler = intHandler; 
    sigaction(SIGINT, &act, NULL); 

    while (keepRunning) { 
     // main loop 
    } 
} 
+0

Cound si prega di inviare un codice praticabile? –

+1

[Dev'essere 'volatile sig_atomic_t' per essere ben definito] (https://stackoverflow.com/questions/37631153/how-to-clean-up-local-data-in-sigint-handler#comment62744362_37631288) – Eric

1
#include<stdio.h> 
#include<signal.h> 
#include<unistd.h> 

void sig_handler(int signo) 
{ 
    if (signo == SIGINT) 
    printf("received SIGINT\n"); 
} 

int main(void) 
{ 
    if (signal(SIGINT, sig_handler) == SIG_ERR) 
    printf("\ncan't catch SIGINT\n"); 
    // A long long wait so that we can easily issue a signal to this process 
    while(1) 
    sleep(1); 
    return 0; 
} 

I controlli funzione sig_handler se il valore dell'argomento passato è uguale al SIGINT, quindi printf è eseguito.