Fangen Sie Strg-C in C

Lesezeit: 7 Minuten

Feldors Benutzeravatar
Feldor

Wie fängt man Strg+C in C?

  • So etwas wie das Abfangen von Signalen gibt es in C nicht … dachte ich zumindest, bis ich den C99-Standard las. Es stellt sich heraus, dass in C eine Signalbehandlung definiert ist, aber Strg-C ist nicht verpflichtet, ein bestimmtes Signal oder überhaupt ein Signal zu erzeugen. Je nach Plattform ist dies möglicherweise nicht möglich.

    – JeremyP

    18. November 2010 um 16:40 Uhr

  • Die Signalbehandlung ist hauptsächlich implementierungsabhängig. Verwenden Sie auf *nix-Plattformen , und wenn Sie auf OSX sind, können Sie GCD nutzen, um die Dinge noch einfacher zu machen~.

    – Dylan Lukes

    18. November 2010 um 19:09 Uhr

Benutzeravatar von Dirk Eddelbüttel
Dirk Edelbüttel

Mit einem Signalhandler.

Hier ist ein einfaches Beispiel für das Umdrehen von a bool benutzt in main():

#include <signal.h>

static volatile int keepRunning = 1;

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

// ...

int main(void) {

   signal(SIGINT, intHandler);

   while (keepRunning) { 
      // ...

Bearbeitung im Juni 2017: An wen es betreffen mag, insbesondere an diejenigen mit einem unersättlichen Drang, diese Antwort zu bearbeiten. Schau, ich habe diese Antwort geschrieben Sieben Jahre zuvor. Ja, Sprachstandards ändern sich. Wenn Sie wirklich die Welt verbessern müssen, fügen Sie bitte hinzu deine neue Antwort aber lass meine so wie sie ist. Da die Antwort meinen Namen enthält, würde ich es vorziehen, wenn sie auch meine Worte enthält. Vielen Dank.

  • Lassen Sie uns erwähnen, dass wir #einschließen müssen, damit dies funktioniert!

    – kristianlm

    4. September 2011 um 14:36 ​​Uhr


  • Wie wäre es mit #include <stdbool.h>? 🙂

    – Labyrinth

    14. Dezember 2012 um 12:23 Uhr

  • statisch Es sollte sein bool volatile keepRunning = true; um 100% sicher zu sein. Der Compiler kann frei zwischengespeichert werden keepRunning in einem Register und die Volatilität wird dies verhindern. In der Praxis funktioniert es höchstwahrscheinlich auch ohne das Schlüsselwort volatile, wenn die While-Schleife mindestens eine Nicht-Inline-Funktion aufruft.

    – Johannes Übermann

    20. Mai 2013 um 11:05 Uhr

  • @DirkEddelbuettel Ich stehe korrigiert, ich dachte, meine Verbesserungen werden Ihre ursprünglichen Absichten mehr widerspiegeln, sorry, wenn das nicht passiert ist. Wie auch immer, das größere Problem ist, dass, da Ihre Antwort versucht, allgemein genug zu sein, und weil sie IMO einen Ausschnitt liefern sollte, der auch unter asynchronen Interrupts funktioniert: Ich würde entweder verwenden sig_atomic_t oder atomic_bool Typen dort. Das habe ich gerade verpasst. Jetzt, wo wir gerade reden: Möchten Sie, dass ich meine letzte Bearbeitung rückgängig mache? Keine harten Gefühle da, es wäre aus Ihrer Sicht vollkommen verständlich 🙂

    – Peter Varo

    18. Mai 2017 um 22:51 Uhr

  • Das ist viel besser!

    – Dirk Edelbüttel

    19. Mai 2017 um 10:59 Uhr

Benutzeravatar von icyrock.com
icyrock.com

Überprüfe hier:

Notiz: Offensichtlich ist dies ein einfaches Beispiel zur Erklärung nur wie man a einrichtet StrgC Handler, aber wie immer gibt es Regeln, die befolgt werden müssen, um nicht etwas anderes zu brechen. Bitte lesen Sie die Kommentare unten.

Der Beispielcode von oben:

#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
}

  • @Derrick Zustimmung, int main ist eine ordentliche Sache, aber gcc und andere Compiler kompilieren dies seit 1990 zu korrekt laufenden Programmen. Hier ganz gut erklärt: eskimo.com/~scs/readings/voidmain.960823.html – es ist im Grunde ein “Feature”, ich nehme es so.

    – icyrock.com

    18. November 2010 um 16:32 Uhr

  • @icyrock.com: Alles sehr wahr (in Bezug auf void main() in C), aber wenn Sie öffentlich posten, ist es wahrscheinlich besser, die Debatte ganz zu vermeiden, indem Sie int main() verwenden, damit sie nicht vom Hauptpunkt ablenkt.

    – Clifford

    18. November 2010 um 17:08 Uhr

  • Darin liegt ein großer Fehler. Sie können printf nicht sicher innerhalb des Inhalts eines Signalhandlers verwenden. Es ist eine Verletzung der asynchronen Signalsicherheit. Dies liegt daran, dass printf nicht reentrant ist. Was passiert, wenn das Programm gerade printf verwendet, als Strg-C gedrückt wurde, und Ihr Signal-Handler gleichzeitig damit beginnt, es zu verwenden? Hinweis: Es wird wahrscheinlich brechen. write und fwrite sind in diesem Zusammenhang gleich zu verwenden.

    – Dylan Lukes

    18. November 2010 um 19:07 Uhr


  • @icyrock.com: Etwas Kompliziertes in einem Signal-Handler zu machen, wird Kopfschmerzen verursachen. Vor allem mit dem io-System.

    – Martin York

    18. November 2010 um 19:50 Uhr

  • @stacker Danke – ich denke, es lohnt sich am Ende. Wenn jemand in Zukunft über diesen Code stolpert, ist es besser, ihn so weit wie möglich richtig zu machen, unabhängig vom Thema der Frage.

    – icyrock.com

    18. November 2010 um 23:10 Uhr

Benutzeravatar von Filip J
Philipp J.

Ergänzung zu UN*X-Plattformen.

Laut dem signal(2) Manpage zu GNU/Linux, das Verhalten von signal ist nicht so tragbar wie das Verhalten von sigaction:

Das Verhalten von signal() variiert zwischen UNIX-Versionen und hat sich auch historisch zwischen verschiedenen Linux-Versionen verändert. Vermeiden Sie seine Verwendung: Verwenden Sie stattdessen sigaction(2).

Auf System V blockierte das System die Lieferung weiterer Instanzen des Signals nicht und die Lieferung eines Signals würde den Handler auf den Standardwert zurücksetzen. In BSD hat sich die Semantik geändert.

Die folgende Variante der vorherigen Antwort von Dirk Eddelbüttel verwendet sigaction Anstatt von 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
    }
}

  • Muss sein volatile sig_atomic_t wohldefiniert sein

    – Erich

    4. Januar 2018 um 21:43 Uhr

@Peter Varo hat Dirks Antwort aktualisiert, aber Dirk hat die Änderung abgelehnt. Hier die neue Antwort von Peter:

Obwohl das obige Snippet ein korrektes c89-Beispiel ist, sollte man nach Möglichkeit die moderneren Typen und Garantien verwenden, die von den späteren Standards bereitgestellt werden. Daher ist hier eine sicherere und modernere Alternative für diejenigen, die nach einer c99- und c11-konformen Implementierung suchen:

#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 Die Überschrift <signal.h> deklarieren Sie einen Typ … sig_atomic_t Dies ist der (möglicherweise flüchtig qualifizierte) ganzzahlige Typ eines Objekts, auf das als atomare Entität zugegriffen werden kann, selbst bei Vorhandensein asynchroner Interrupts.

Außerdem:

C11-Standard: 7.14.1.1§5 Wenn das Signal anders als als Ergebnis des Anrufs auftritt abort oder raise Funktion ist das Verhalten undefiniert, wenn der Signalhandler auf irgendein Objekt mit verweist static oder Thread-Speicherdauer, die kein lock-freies atomares Objekt ist, außer durch Zuweisen eines Werts zu einem als deklarierten Objekt volatile sig_atomic_t

Benutzeravatar von Walter
Walter

Oder Sie können das Terminal wie folgt in den Raw-Modus versetzen:

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

Jetzt sollte es möglich sein, zu lesen Strg+C Tastenanschläge verwenden fgetc(stdin). Hüten Sie sich jedoch davor, dies zu verwenden, da dies nicht möglich ist Strg+Z, Strg+Q, Strg+Setc. wie normalerweise auch nicht mehr.

Richten Sie eine Falle ein (Sie können mehrere Signale mit einem Handler abfangen):

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

Behandeln Sie das Signal wie Sie wollen, aber seien Sie sich der Einschränkungen und Fallstricke bewusst:

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

Benutzeravatar von Clifford
Clifford

Beachten Sie in Bezug auf vorhandene Antworten, dass die Signalverarbeitung plattformabhängig ist. Win32 zum Beispiel verarbeitet weit weniger Signale als POSIX-Betriebssysteme; siehe hier. Während SIGINT unter Win32 in Signals.h deklariert ist, lesen Sie den Hinweis in der Dokumentation, der erklärt, dass es nicht das tun wird, was Sie vielleicht erwarten.

1425680cookie-checkFangen Sie Strg-C in C

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy