Kann ein C-Programm die Ausführung fortsetzen, nachdem ein Signal behandelt wurde?

Lesezeit: 6 Minuten

Ich bin neu in der Signalverarbeitung in Unix bis C und habe mir einige Tutorials dazu angesehen (aus reinem Interesse).

Meine Frage ist, ist es möglich, die Ausführung eines Programms über den Punkt hinaus fortzusetzen, an dem ein Signal verarbeitet wird?

Ich verstehe, dass die Signalbehandlungsfunktion die Bereinigung durchführt, aber ist es im Sinne der Ausnahmebehandlung (wie in C++) möglich, dass dieses Signal auf die gleiche Weise behandelt wird und das Programm normal weiterläuft?

In dem Augenblick catch geht in einer Endlosschleife (vermutlich wäre ein Weg zum Beenden ein Call exit(1) ).

Meine Absicht wäre für b 1 zuzuweisen und das Programm ordnungsgemäß zu beenden (wenn das natürlich möglich ist).

Hier ist mein Code:

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

int a = 5;
int b = 0;

void catch(int sig)
{
    printf("Caught the signal, will handle it now\n");
    b = 1;
}

int main(void)
{
    signal(SIGFPE, catch);

    int c = a / b;

    return 0;
}

Da C prozedural ist, wie kommt es außerdem, dass der vor der störenden Anweisung deklarierte Signalhandler tatsächlich aufgerufen wird, nachdem die letztere ausgeführt wurde?

Und schließlich müssen alle Variablen, die im Falle einer Ausnahme bereinigt werden müssen, vor der Funktion deklariert werden, damit die Behandlungsfunktion ordnungsgemäß bereinigt werden kann, oder?

Vielen Dank im Voraus für Ihre Antworten und Entschuldigung, wenn einige der oben genannten Punkte sehr offensichtlich sind.

  • Häufiger Anwendungsfall: Viele (Daemon-)Prozesse können mit a umgehen SIGHUP Signal, wodurch sie die Konfigurationsdatei neu laden.

    – Frerich Raabe

    9. Januar 2013 um 10:52 Uhr

Benutzeravatar von Jonas Schäfer
Jonas Schäfer

Ja, dafür sind Signalhandler da. Einige Signale müssen jedoch speziell behandelt werden, damit das Programm fortgesetzt werden kann (z. B. SIGSEGV, SIGFPE, …).

Siehe die Manpage von sigaction:

Laut POSIX ist das Verhalten eines Prozesses undefiniert, nachdem er ein SIGFPE-, SIGILL- oder SIGSEGV-Signal ignoriert, das nicht von kill(2) oder raise(3) erzeugt wurde. Die ganzzahlige Division durch Null hat ein undefiniertes Ergebnis. Auf einigen Architekturen wird ein SIGFPE-Signal generiert. (Auch das Teilen der negativsten Ganzzahl durch -1 kann SIGFPE erzeugen.) Das Ignorieren dieses Signals kann zu einer Endlosschleife führen.

Jetzt gerade du sind Ignorieren des Signals, indem Sie nichts tun, um zu verhindern, dass es (wieder) passiert. Sie benötigen den Ausführungskontext im Signalhandler und reparieren ihn manuell, was das Überschreiben einiger Register beinhaltet.

Wenn SA_SIGINFO in sa_flags angegeben ist, dann gibt sa_sigaction (anstelle von sa_handler) die Signalverarbeitungsfunktion für signum an. Diese Funktion erhält als erstes Argument die Signalnummer, als zweites Argument einen Zeiger auf siginfo_t und als drittes Argument einen Zeiger auf ucontext_t (cast to void *). (Normalerweise verwendet die Handler-Funktion das dritte Argument nicht. Siehe getcontext(2) für weitere Informationen über ucontext_t.)

Der Kontext ermöglicht den Zugriff auf die Register zum Zeitpunkt des Fehlers und muss geändert werden, damit Ihr Programm fortgesetzt werden kann. Sieh dir das an lkml-Beitrag. Wie dort erwähnt, siglongjmp wäre vielleicht auch eine Option. Der Beitrag bietet auch eine ziemlich wiederverwendbare Lösung für die Behandlung des Fehlers, ohne Variablen global machen zu müssen usw.:

Und weil Sie es selbst handhaben, haben Sie bei der Fehlerbehandlung jede gewünschte Flexibilität. Beispielsweise können Sie den Fehlerbehandler wie folgt zu einem bestimmten Punkt in Ihrer Funktion springen lassen:

 __label__ error_handler;   
 __asm__("divl %2"      
         :"=a" (low), "=d" (high)       
         :"g" (divisor), "c" (&&error_handler))     
 ... do normal cases ...

 error_handler:     
     ... check against zero division or overflow, so  whatever you want to ..

Dann muss Ihr Handler für SIGFPE nur so etwas tun wie

Kontext.eip = Kontext.ecx;

  • Vielen Dank für die informative Antwort. Ich habe etwas ähnliches mit gemacht SIGALRM und es hat die Ausnahme abgefangen und ist normal weitergegangen, wusste das nicht SIGFPE war besonders. Wenn es nicht zu viel verlangt ist, könnten Sie mir bitte kurz sagen, wie ich den Code in den Beitrag integrieren muss, um diese Ausnahme zu behandeln? Ich nehme an, ich müsste den Assemblercode in deklarieren main Routine, aber ich bin mir nicht sicher, wo ich das Etikett oder die anbringen soll sigfpe_handler Funktion, die im Beitrag erwähnt wird (ich denke, ich kann sie nicht als Argument angeben signal da das Zeigerargument nur das Signal int als Argument akzeptiert).

    – Nobilis

    9. Januar 2013 um 12:36 Uhr

  • Sie müssen verwenden sigaction anstelle von signal. Das sigaction struct ist vielseitiger in der Anzahl der Optionen, die es bietet. Für den anderen Teil lesen Sie bitte den lkml-Beitrag zweimal, ich glaube nicht, dass ich diesen Code für Sie schreiben kann.

    – Jonas Schäfer

    9. Januar 2013 um 20:38 Uhr

  • Das wäre okay, danke, brauchte nur ein paar Informationen zur Verwendung.

    – Nobilis

    10. Januar 2013 um 8:16 Uhr

Wenn Sie wissen, was Sie tun, können Sie den Anweisungszeiger so einstellen, dass er direkt hinter die betreffende Anweisung zeigt. Unten ist mein Beispiel für x86 (32bit und 64bit). Versuchen Sie es nicht zu Hause oder in echten Produkten !!!

#define _GNU_SOURCE /* Bring REG_XXX names from /usr/include/sys/ucontext.h */

#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <ucontext.h>

static void sigaction_segv(int signal, siginfo_t *si, void *arg)
{
    ucontext_t *ctx = (ucontext_t *)arg;

    /* We are on linux x86, the returning IP is stored in RIP (64bit) or EIP (32bit).
       In this example, the length of the offending instruction is 6 bytes.
       So we skip the offender ! */
    #if __WORDSIZE == 64
        printf("Caught SIGSEGV, addr %p, RIP 0x%lx\n", si->si_addr, ctx->uc_mcontext.gregs[REG_RIP]);
        ctx->uc_mcontext.gregs[REG_RIP] += 6;
    #else
        printf("Caught SIGSEGV, addr %p, EIP 0x%x\n", si->si_addr, ctx->uc_mcontext.gregs[REG_EIP]);
        ctx->uc_mcontext.gregs[REG_EIP] += 6;
    #endif
}

int main(void)
{
    struct sigaction sa;

    memset(&sa, 0, sizeof(sa));
    sigemptyset(&sa.sa_mask);
    sa.sa_sigaction = sigaction_segv;
    sa.sa_flags = SA_SIGINFO;
    sigaction(SIGSEGV, &sa, NULL);

    /* Generate a seg fault */
    *(int *)NULL = 0;

    printf("Back to normal execution.\n");

    return 0;
}

  • Ich bin interessiert, ob es eine erweiterte RIP-Funktion gibt, da x86 über einen Befehlssatz mit unterschiedlicher Länge verfügt.

    – Bruce Shen

    21. März 2020 um 22:36 Uhr

Im Allgemeinen wird die Ausführung fortgesetzt, nachdem der Handler zurückgekehrt ist. Aber wenn das Signal war verursacht B. durch einen Hardwarefehler (z. B. eine Gleitkomma-Ausnahme oder einen Segmentierungsfehler), haben Sie keine Möglichkeit, diesen Fehler rückgängig zu machen, und daher wird Ihr Programm trotzdem beendet.

Mit anderen Worten, Sie müssen zwischen Signalen und Dingen unterscheiden, die Signale verursachen. Signale an sich sind vollkommen in Ordnung und handhabbar, aber sie lassen Sie nicht immer reparieren Fehler, die Signale verursachen.

(Einige Signale sind speziell, wie z. B. ABRT und STOP, in dem Sinne, dass selbst wenn Sie ein solches Signal nur manuell mit anheben kill, können Sie immer noch nicht “seine Auswirkungen verhindern”. Und natürlich kann KILL überhaupt nicht gehandhabt werden.)

  • SIGSTOP kann nicht gefangen oder ignoriert werden, genau wie SIGKILL.

    – Demi

    13. Juli 2013 um 20:43 Uhr

  • Ihre Antwort wirft einen bemerkenswerten Punkt auf: “Unterscheiden Sie zwischen Signalen und Dingen, die Signale verursachen”. Danke.

    – Pfd

    4. Februar 2017 um 10:35 Uhr

1437300cookie-checkKann ein C-Programm die Ausführung fortsetzen, nachdem ein Signal behandelt wurde?

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

Privacy policy