web-dev-qa-db-fra.com

Catch Ctrl-C dans C

Comment on attrape Ctrl+C en C?

139
Feldor

Avec un gestionnaire de signal.

Voici un exemple simple retournant une bool utilisée dans main():

#include <signal.h>

static volatile int keepRunning = 1;

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

// ...

int main(void) {

   signal(SIGINT, intHandler);

   while (keepRunning) { 
      // ...

Edit in June 2017: À qui de droit, en particulier ceux qui ont un besoin insatiable d’éditer cette réponse. Regardez, j'ai écrit cette réponse sept il y a des années. Oui, les normes linguistiques changent. Si vous devez vraiment améliorer le monde, veuillez ajouter votre nouvelle réponse mais laissez la mienne telle quelle. Comme la réponse porte mon nom, je préférerais qu'elle contienne également mes mots. Je vous remercie.

186
Dirk Eddelbuettel

Vérifiez ici:

Remarque: Évidemment, il s'agit d'un exemple simple expliquant simplement comment définir un a CtrlC gestionnaire, mais comme toujours il y a des règles qui doivent être respectées afin de ne pas casser autre chose. S'il vous plaît lire les commentaires ci-dessous.

Le code exemple ci-dessus:

#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
}
43
icyrock.com

Addendum concernant les plates-formes UN * X.

Selon la page de manuel signal(2) sous GNU/Linux, le comportement de signal n’est pas aussi portable que celui de sigaction:

Le comportement de signal () varie selon les versions UNIX et a également toujours varié d'une version à l'autre de Linux. Evitez son utilisation: utilisez plutôt sigaction (2).

Sous System V, le système ne bloquait pas la livraison d'autres instances du signal et la livraison d'un signal réinitialiserait le gestionnaire à celui par défaut. Dans BSD, la sémantique a changé.

La variante suivante de réponse précédente de Dirk Eddelbuettel utilise sigaction à la place de 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
    }
}
28
Filip J.

Ou vous pouvez mettre le terminal en mode brut, comme ceci:

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);

Maintenant, il devrait être possible de lire Ctrl+C Appuyez sur la touche en utilisant fgetc(stdin). Attention, utilisez ceci car vous ne pouvez pas Ctrl+ZCtrl+QCtrl+S, etc. comme d'habitude non plus non plus.

11
Walter

Mettre en place une interruption (vous pouvez intercepter plusieurs signaux avec un seul gestionnaire):

signal 
 (SIGQUIT, mon_handler); signal 
 (SIGINT, mon_handler); 

Traitez le signal comme vous le souhaitez, mais tenez compte des limites et des pièges:

 void my_handler (int sig) 
 {
/* Votre code ici. */
} 
10
Paul Beckingham

En ce qui concerne les réponses existantes, notez que la gestion du signal dépend de la plate-forme. Win32, par exemple, gère beaucoup moins de signaux que les systèmes d’exploitation POSIX; voir ici . Alors que SIGINT est déclaré dans signaux.h sur Win32, consultez la remarque dans la documentation qui explique qu’il ne fera pas ce que vous pourriez attendre.

4
Clifford
#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;
}

La fonction sig_handler vérifie si la valeur de l'argument passé est égale à SIGINT, puis le printf est exécuté.

3
Love Bisaria

Cela vient d’imprimer avant de sortir.

#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);
}
2
alemol

@ Peter Varo a mis à jour la réponse de Dirk, mais ce dernier a rejeté le changement. Voici la nouvelle réponse de Peter:

Bien que l'extrait de code ci-dessus soit un exemple correct c89 , il convient d'utiliser si possible les types les plus modernes et les garanties fournies par les normes ultérieures. Par conséquent, voici une alternative plus sûre et moderne pour ceux qui recherchent une implémentation conforme c99 et c11 :

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

static volatile sig_atomic_t keep_running = 1;

static void sig_handler(int _)
{
    (void)_;
    keep_running = 0;
}

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

    while (keep_running)
        puts("Still running...");

    puts("Stopped by signal `SIGINT'");
    return EXIT_SUCCESS;
}

C11 Standard: 7.14§2 L'en-tête <signal.h> déclare un type ... sig_atomic_t qui est le (éventuellement qualifié de volatile). ) type entier d'un objet accessible en tant qu'entité atomique, même en présence d'interruptions asynchrones.

En outre:

C11 Standard: 7.14.1.1§5 Si le signal se produit autrement qu'à la suite de l'appel de la fonction abort ou raise , le comportement est indéfini si le gestionnaire de signal fait référence à un objet avec static ou à une durée de stockage de thread qui n'est pas un objet atomique sans verrou, autrement qu'en affectant une valeur à un objet déclaré comme volatile sig_atomic_t .. .

2
MCCCS