Comment on attrape Ctrl+C en C?
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.
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
}
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
}
}
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+Z, Ctrl+Q, Ctrl+S, etc. comme d'habitude non plus non plus.
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. */ }
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.
#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é.
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);
}
@ 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
ouraise
, le comportement est indéfini si le gestionnaire de signal fait référence à un objet avecstatic
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é commevolatile sig_atomic_t
.. .