Ich habe mein Bestes versucht, dies selbst herauszufinden, aber ich möchte wirklich nicht weiter an Dingen herumspielen, die ich nicht vollständig verstehe. Für eine Programmieraufgabe, die ich in C ausführen muss, muss ich ein Programm beenden, wenn der Benutzer die Tastenkombination STRG + D über ein Terminal eingibt. Ich habe versucht, diese Funktionalität in einer kleineren Testfunktion zu isolieren, aber jetzt verhält sich meine STRG + D wie meine STRG + C und STRG + C hat auch außerhalb des Programms keine Auswirkungen, wenn die Ausführung abgeschlossen ist. Dies ist das Programm, das diese Änderung verursacht hat:

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

void ctrlD(int sig){ 
  printf("\n");
  signal(SIGINT, SIG_DFL);
  exit(0); 
}

int main(){
  signal(SIGINT, ctrlD);
  while(1) {
    printf("Hello\n");
    sleep(5);
  }
}

Die Zeile signal(SIGINT, SIG_DFL); wurde später hinzugefügt, als festgestellt wurde, dass meine STRG + C nicht mehr funktionierte. Ich dachte, es würde die Tastenanschläge auf ihre ursprünglichen Funktionen zurücksetzen, aber ohne Erfolg. Was kann ich tun, um die ursprünglichen Funktionen wiederherzustellen und gleichzeitig dafür zu sorgen, dass dieses Programm mit STRG + D funktioniert?

*** EDIT: Diese Frage scheint ein wenig von den Schienen geraten zu sein. Ich verstehe jetzt, dass Strg + D kein Signal ist. Trotzdem habe ich nicht mehr die Funktionalität von Strg + C , wenn ich versuche, es in meinem MAC OS-Terminal zu verwenden, und stattdessen Strg + < kbd> D scheint genau diese Funktionalität zu haben. WIE genau kann ich jeden zurückgeben, um die Funktionalität zu haben, die er hatte, bevor ich mich auf diese zufällige Reise begab?

1
jmglynn 18 Apr. 2018 im 07:14

4 Antworten

Beste Antwort

Vielen Dank an alle, die zu dieser Frage beigetragen haben. Die bereitgestellten / verknüpften Ressourcen waren enorm hilfreich, um mehr über Signale zu erfahren (und dass EOF kein Signal ist), neben der anderen Fülle an bereitgestellten Informationen.

Nach weiteren Recherchen stellte ich fest, dass ich entweder durch einen fehlgeschlagenen versehentlichen Bash-Befehl oder durch das in meiner ursprünglichen Frage selbst veröffentlichte Programm die Tastenzuordnungen für die Stty-Einstellungen meines Terminals geändert hatte. Wenn sich in Zukunft jemand in dieser seltsam spezifischen Situation befindet, hoffe ich, dass dies hilfreich sein kann, da dies mein Problem behoben hat:

Geben Sie den Befehl $ stty -a ein, um alle Einstellungen Ihrer Terminals anzuzeigen, insbesondere den Abschnitt "cchars".

Ich sah dann die Umkehrung und reparierte sie so:

$ stty intr ^C
$ stty eof ^D

Anschließend können Sie $ stty -a erneut ausführen, um festzustellen, ob die Änderungen ordnungsgemäß wirksam wurden. Nochmals vielen Dank an alle.

0
jmglynn 19 Apr. 2018 im 01:07

Wenn Sie beabsichtigen, das Standardverhalten des Signals nach dem Ausführen des Handlers wiederherzustellen, übergeben Sie das SA_RESETHAND-Flag an sa_flags, während Sie die Signalaktion registrieren. Zum Beispiel.

struct sigaction act;
memset(&act, 0, sizeof(struct sigaction));
act.sa_flags = SA_RESETHAND;
act.sa_handler = some_handler;
sigaction(SIGINT, &act, NULL);

Von sigaction () man

SA_RESETHAND

Stellen Sie die Signalaktion beim Aufrufen des Signalhandlers auf den Standardwert zurück. Dieses Flag ist nur beim Einrichten eines Signalhandlers von Bedeutung.

3
Monk 18 Apr. 2018 im 11:47

Wenn Sie ein Programm schreiben, um Signale zu untersuchen, ist es viel besser, es sorgfältig unter Verwendung geeigneter POSIX-Schnittstellen (sigaction() anstelle von signal()) und Vermeidung von undefiniertem Verhalten (unter Verwendung von non- sichere Funktionen für asynchrone Signale in einem Signalhandler).

Betrachten Sie zum Beispiel das folgende Programm:

#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>

static volatile sig_atomic_t  sigint_count = 0;

static void catch_sigint(int signum)
{
    if (signum == SIGINT)
        sigint_count++;
}

static int install_sigint(void)
{
    struct sigaction  act;

    memset(&act, 0, sizeof act);
    sigemptyset(&act.sa_mask);

    act.sa_handler = catch_sigint;
    act.sa_flags = 0;

    if (sigaction(SIGINT, &act, NULL) == -1)
        return errno;

    return 0;
}

static int install_default(const int signum)
{
    struct sigaction  act;

    memset(&act, 0, sizeof act);
    sigemptyset(&act.sa_mask);

    act.sa_handler = SIG_DFL;
    act.sa_flags = 0;

    if (sigaction(signum, &act, NULL) == -1)
        return errno;

    return 0;
}

int main(void)
{
    struct timespec  duration;
    int              result;

    if (install_sigint()) {
        fprintf(stderr, "Cannot install SIGINT handler: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    duration.tv_sec = 5;
    duration.tv_nsec = 0; /* 1/1000000000ths of a second. Nine zeroes. */

    printf("Sleeping for %d seconds.\n", (int)duration.tv_sec);
    fflush(stdout);
    while (1) {

        result = nanosleep(&duration, &duration);
        if (!result)
            break;

        if (errno != EINTR) {
            fprintf(stderr, "nanosleep() failed: %s.\n", strerror(errno));
            return EXIT_FAILURE;
        }

        /* nanosleep was interrupted by a delivery of a signal. */

        if (sigint_count >= 3) {
            /* Ctrl+C pressed three or more times. */

            if (install_default(SIGINT) == -1) {
                fprintf(stderr, "Cannot revert SIGINT to the default handler: %s.\n", strerror(errno));
                return EXIT_FAILURE;
            }

            printf("SIGINT has been reverted to the default handler.\n");
            fflush(stderr);
        }
    }

    if (sigint_count > 0)
        printf("You pressed Ctrl+C %d time%s.\n", (int)sigint_count, (sigint_count > 1) ? "s" : "");
    else
        printf("You did not press Ctrl+C at all.\n");

    return EXIT_SUCCESS;
}

Das #define teilt Ihrer C-Bibliothek (insbesondere glibc) mit, dass Sie POSIX.1-2008-Funktionen (und höher) von ihr erhalten möchten.

Der INT Signalhandler erhöht nur einen volatile sig_atomic_t Zähler. Beachten Sie, dass dieser Typ möglicherweise einen sehr kleinen Bereich hat, den er darstellen kann. 0 bis einschließlich 127 sollten sicher sein.

Das Hauptprogramm wartet mit POSIX nanosleep() Funktion. Auf einigen Systemen kann sleep() über die Funktion SIGALRM implementiert werden, sodass es besser vermieden wird, wenn Signale anderweitig verwendet werden. nanosleep() stört solche Signale überhaupt nicht. Außerdem kann nanosleep() die verbleibende Zeit zurückgeben, wenn sie durch eine Signalübertragung unterbrochen wird.

In der Hauptschleife gibt nanosleep() 0 zurück, wenn das gesamte Intervall geschlafen hat (beachten Sie jedoch, dass die verbleibende Zeit in diesem Fall möglicherweise nicht auf 0 aktualisiert wird). Wenn es durch die Übermittlung eines Signals unterbrochen wird, gibt es -1 mit errno == EINTR zurück und die verbleibende Zeit wird aktualisiert. (Der erste Zeiger bezieht sich auf die Dauer des Schlafes und der zweite darauf, wo die verbleibende Zeit gespeichert werden soll. Sie können für beide dieselbe Struktur verwenden.)

Normalerweise führt die Hauptschleife nur eine Iteration durch. Es kann mehr als eine Iteration ausführen, wenn es durch die Übermittlung eines Signals unterbrochen wird.

Wenn die Hauptschleife erkennt, dass sigint_count mindestens drei ist, d. H. Mindestens drei INT Signale empfangen hat, setzt sie den Signalhandler auf die Standardeinstellungen zurück.

(Beachten Sie, dass sowohl memset() als auch sigemptyset() wichtig sind, wenn Sie die struct sigaction -Struktur löschen. Das memset() stellt sicher, dass zukünftiger Code abwärtskompatibel mit älterem Code ist, indem ein gleichmäßiges Auffüllen sichergestellt wird Felder werden gelöscht. Und sigemptyset() ist der sichere Weg, um die Signalmaske zu löschen (Satz von Signalen, die blockiert werden, während der Handler ausgeführt wird).)

(Theoretisch ist memset() nicht asynchron für Signale geeignet, während dies sowohl sigemptyset() als auch sigaction() sind. Deshalb habe ich den Signalhandler im Hauptprogramm und nicht im Signal zurückgesetzt Handler.)

Wenn Sie von einem Signalhandler aus drucken möchten, müssen Sie Low-Level-E / A verwenden, da <stdio.h> -Funktionen nicht asynchron signalensicher sind. Mit der folgenden Funktion können Sie beispielsweise Zeichenfolgen in die Standardausgabe drucken:

static int wrerr(const char *p)
{
    const int  saved_errno = errno;
    int        retval = 0;

    if (p) {
        const char *q = p;
        ssize_t     n;

        while (*q)
            q++;

        while (p < q) {
            n = write(STDERR_FILENO, p, (size_t)(q - p));
            if (n > 0)
                p += n;
            else
            if (n != -1) {
                retval = EIO;
                break;
            } else
            if (errno != EINTR) {
                retval = errno;
                break;
            }
        }
    }

    errno = saved_errno;
    return retval;
}

Die obige wrerr() -Funktion ist async-signal-sicher (da sie selbst nur async-signal-sichere Funktionen verwendet) und behält sogar errno unverändert bei. (Viele Anleitungen vergessen zu erwähnen, dass es für einen Signalhandler sehr wichtig ist, errno unverändert zu lassen. Andernfalls, wenn eine Funktion von einem Signalhandler unterbrochen wird und dieser Signalhandler errno ändert, wird die ursprüngliche Funktion geändert gibt -1 zurück, um einen Fehler anzuzeigen, aber dann ist errno nicht mehr EINTR!)

Sie können einfach wrerr("INT signal!\n") verwenden, wenn Sie möchten. Der Rückgabewert von wrerr() ist Null, wenn der Schreibvorgang erfolgreich war, andernfalls ein Fehlercode. Es ignoriert Interrupts selbst.

Beachten Sie, dass Sie die stderr Ausgabe nicht über fprintf() oder andere <stdio.h> Funktionen mit den oben genannten Funktionen mischen sollten (außer möglicherweise zum Drucken von Fehlermeldungen, wenn das Programm abgebrochen wird). Das Mischen ist kein undefiniertes Verhalten, sondern kann nur zu überraschenden Ergebnissen führen, z. B. zur Ausgabe von wrerr() inmitten einer Ausgabe von fprintf(stderr,...).

0
Nominal Animal 18 Apr. 2018 im 05:22

Es liegt an der Anweisung exit(0) im Handler, wenn SIGINT ausgelöst wird, wird der Handler strlD aufgerufen und Sie denken vielleicht, warum signal(SIGINT,SIG_DFL) nicht funktioniert hat? Eigentlich funktioniert es. Ihr Hauptprozess a.out wird dort jedoch selbst erfolgreich beendet, indem Sie exit(0) aufrufen. Entfernen Sie exit(0), wenn Sie das Verhalten von SIGINT wiederherstellen möchten.

#include <unistd.h> 
#include <stdio.h> 
#include <termios.h> 
#include <signal.h> 
#include <stdlib.h>
void ctrlD(int sig){ 
        //printf("CTRL+C pressed\n");/* just to observe I added one printf
                   statement, Ideally there shouldn't be any printf here */
        signal(SIGINT, SIG_DFL);/*restoring back to original action */
}
int main(){
        signal(SIGINT, ctrlD);/*1st time when CTRL+C pressed, handler ctrlD gets called */
        while(1) {
                printf("Hello\n");
                sleep(5);
        }
        return 0;
}

Es ist auch ratsam, sigaction() anstelle von signal() zu verwenden, wie hier angegeben. Was ist der Unterschied zwischen Sigaction und Signal?. Lesen Sie man 2 sigaction und man 2 exit, um zu überprüfen, was exit(0) bedeutet.

Auch dies Wie vermeide ich die Verwendung von printf in einem Signalhandler?

Bearbeiten :

void ctrlD(int sig){
        /* printf("CTRL+C pressed \n"); */
        signal(SIGINT, SIG_DFL); /* only one time CTRL+C works
                               after that SIG_DFL will terminate whole process */
}
int main(){
        signal(SIGINT, ctrlD); /* if you press CTRL+C then it will go to handler
                                and terminate */
        int ch;
        while( ((ch = getchar())!=EOF) ) { /* wait or read char until CTrl+D is not pressed  */
                printf("Hello : %d \n",ch);/* ASCII equivalent of char */
        }
        return 0;
}
0
Achal 18 Apr. 2018 im 06:21