Kann exit() den Prozess nicht beenden?

Lesezeit: 7 Minuten

Ich habe einen Signalhandler in meinem Programm registriert. Beim Empfang eines unerwünschten Signals (SIGABRT) rufe ich „exit(-1)“ im Signalhandler auf, um den Prozess zu beenden. Aber wie bei einigen Gelegenheiten bemerkt, ruft es exit () auf, kann den Prozess jedoch nicht beenden.

Das Problem wurde zufällig generiert und ich vermute stark bei der Ausführung von exit().

Kann es Gründe oder Fälle geben, in denen exit() den Prozess nicht beenden kann?

Danke.

  • Ich rate Ihnen auf jeden Fall, nach einer anderen Ursache für den Fehler als einer Funktion wie zu suchen exit. Fast immer, wenn Sie denken, dass es einen Fehler im Compiler oder in der Standardbibliothek usw. gibt, ist es Ihr eigener Fehler, der den Fehler verursacht.

    – Shahbaz

    12. Januar 2012 um 10:24 Uhr

  • @Shahbaz: Mandar fragt nicht nach einem Fehler in der Implementierung oder deutet an, dass einer existiert. Die Frage ist ob exit wird angegeben, um das Programm immer zu beenden, und die Antwort ist nein (und insbesondere nicht, wenn es von einem Signal-Handler aufgerufen wird).

    – Steve Jessop

    12. Januar 2012 um 11:10 Uhr


  • Warum nicht einfach den Handler für SIGABRT auf exit() setzen? Was auch immer Sie sonst im Signal-Handler tun, tun Sie es in einem atexit()-Aufruf.

    – William Pursel

    12. Januar 2012 um 17:24 Uhr

  • @William Pursell: Wenn ein Prozess durch ein Signal beendet werden soll, werden von atexit() registrierte Funktionen nicht aufgerufen, es sei denn, jemand war dumm genug, exit() vom Signalhandler aufzurufen. Der Wiedereintritt ist eine Pille.

    – Josua

    14. Juni 2013 um 16:21 Uhr

Rufst du an exit() vom Signalhandler?

In man 7 signalAbschnitt Async-signalsichere Funktionen Sie können alle Funktionen sehen, die garantiert funktionieren, wenn sie von einem Signalhandler aufgerufen werden:

Eine Signalbehandlungsfunktion muss sehr vorsichtig sein, da die Verarbeitung an anderer Stelle an einem beliebigen Punkt in der Ausführung des Programms unterbrochen werden kann. POSIX hat das Konzept der “sicheren Funktion”. Wenn ein Signal die Ausführung einer unsicheren Funktion unterbricht und der Handler eine unsichere Funktion aufruft, ist das Verhalten des Programms undefiniert.

POSIX.1-2004 (auch bekannt als POSIX.1-2001 Technical Corrigendum 2) erfordert eine Implementierung, um zu gewährleisten, dass die folgenden Funktionen innerhalb eines Signalhandlers sicher aufgerufen werden können:

Da sieht man Funktionen _Exit(), _exit() Und abort()aber vor allem nicht exit(). Sie sollten es also nicht von einem Signalhandler aufrufen.

Das Schlimme ist, dass selbst wenn Sie eine unsichere Funktion von einem Signal-Handler aufrufen (printf() irgendwelche?) es wird einfach die meiste Zeit funktionieren … aber nicht immer.

  • Das Aufrufen einer asynchronen signalunsicheren Funktion von einem Signalhandler ist vollkommen legal es sei denn, das Signal hat eine andere Async-Signal-unsichere Funktion unterbrochen. Dies erklärt den Grund, warum es “meistens” funktioniert, aber es bedeutet auch, dass Sie async-signal-unsafe-Funktionen in Signal-Handlern sogar in korrektem, robustem Code verwenden können, wenn Sie dafür sorgen, dass der Signal-Handler ein unsicheres nicht unterbrechen kann Funktion (zum Beispiel das Signal die meiste Zeit maskiert lassen und es nur entsperren, während reine arithmetische oder asynchrone signalsichere Bibliotheksaufrufe wie Dateideskriptor IO und ausgeführt werden select).

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    12. Januar 2012 um 13:41 Uhr

  • @R.. Du hast natürlich Recht. Aber in der Praxis ist es schwierig, dies zu gewährleisten, außer in ganz besonderen Fällen. Ich persönlich finde es viel bequemer, einfach die unsicheren Funktionen alle zusammen zu vermeiden.

    – rodrigo

    12. Januar 2012 um 13:47 Uhr

  • Siehe auch: pubs.opengroup.org/onlinepubs/009695399/functions/…

    – William Pursel

    12. Januar 2012 um 17:22 Uhr

  • @rodrigo: Es ist eigentlich ziemlich üblich, wenn Sie nur Signale verwenden, um Ihr Programm vom Benutzer unterbrechbar zu machen, während Sie auf IO warten – solange Sie sich die Mühe machen, es zu blockieren und zu entsperren SIGINT oder was auch immer zur richtigen Zeit. Es ist auch üblich in Multithread-Programmen, wenn Sie das Signal in allen außer einem dedizierten Signalverarbeitungs-Thread blockiert lassen.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    12. Januar 2012 um 23:41 Uhr

  • @R .. Ein bisschen spät hier, aber niemand hat den C-Standard selbst erwähnt. “Das Aufrufen einer asynchronen signalunsicheren Funktion von einem Signalhandler ist vollkommen legal …” Ich würde es nicht als “völlig legal” bezeichnen. Per 7.1.4 Nutzung von BibliotheksfunktionenAbsatz 4: “Die Funktionen in der Standardbibliothek sind nicht garantiert wiedereintrittsfähig und können Objekte mit statischer oder Thread-Speicherdauer ändern.” Und Fußnote 188: “Daher kann ein Signalhandler im Allgemeinen keine Standardbibliotheksfunktionen aufrufen.”

    – Andreas Henle

    11. Januar 2019 um 18:39 Uhr

Benutzeravatar von Jeff Foster
Jeff Foster

Ja, es gibt einige Umstände, wie zum Beispiel:

Die Funktion exit() muss zuerst alle von atexit() registrierten Funktionen in der umgekehrten Reihenfolge ihrer Registrierung aufrufen, außer dass eine Funktion nach allen zuvor registrierten Funktionen aufgerufen wird, die zum Zeitpunkt ihrer Registrierung bereits aufgerufen wurden. Jede Funktion wird so oft aufgerufen, wie sie registriert wurde. Wenn während des Aufrufs einer solchen Funktion ein Aufruf der Funktion longjmp() erfolgt, der den Aufruf der registrierten Funktion beenden würde, ist das Verhalten undefiniert.

Wenn eine durch einen Aufruf von atexit() registrierte Funktion nicht zurückkehrt, werden die verbleibenden registrierten Funktionen nicht aufgerufen und der Rest der Verarbeitung von exit() wird nicht abgeschlossen. Wenn exit() mehr als einmal aufgerufen wird, ist das Verhalten undefiniert.

Siehe die POSIX-Seite auf Ausfahrt.

Fügen Sie für weitere Informationen einen Debugger an, wenn Sie die Situation erreichen, und werfen Sie einen Blick auf die Aufrufliste.

Benutzeravatar von jarzec
jarzec

Ich hatte ein analoges Problem wie das von Madar beschriebene. Ich musste für jedes Signal eine Aktion ausführen und richtig beenden. Ich habe mich durch ein paar Antworten auf ähnliche Probleme gewundert und bin auf die folgende Erklärung / Lösung gekommen.

Erläuterung:
Ein Problem ist das exit() sollte nicht in Signalhandlern verwendet werden, da es sich nicht um eine der async-signalsicheren Funktionen handelt (siehe man signal-safety). Dies bedeutet, dass es in Signalhandlern funktionieren kann, aber nicht garantiert ist. Infolgedessen müssten Sie anrufen _exit()/_Exit() (die async-signalsicher sind). Diese beenden den Vorgang jedoch sofort, ohne die aufzurufen atexit Rückrufe und statische Destruktoren. Mein Verständnis ist, dass für einige Signale etwas mehr Reinigung durchgeführt werden kann, als diese Funktionen bieten.

Lösung: Die Lösung, die ich mir ausgedacht habe, besteht darin, Ihren Signal-Handler für alle Signale zu registrieren und alle zusätzlichen Schritte auszuführen. Dann können Sie auf den Standard-Handler zurücksetzen und aufrufen raise(signal_number)das asynchronsignalsicher ist, um das Signal erneut zu senden und so den Standardhandler auszuführen.

Hier ist ein funktionierendes Beispiel, das nur den Standardhandler ausführt SIGINT. Ich denke, das ist zu einfach, um das “Scheitern” zu erfahren exit() wenn Sie es im Handler verwendet haben. Ich habe ähnlichen Code mit einem alternativen Stack getestet, um ihn ebenfalls zu handhaben SIGSEGV.

Notiz Wenn Sie möchten, dass dies im Kontext mit mehreren Threads ordnungsgemäß funktioniert (z. B. mehrere Threads verursachen SIGSEGV gleichzeitig) müssen Sie sich um die Synchronisation kümmern. Threads teilen sich denselben Handler, haben aber eine separate Signalmaskierung.

#include <csignal>
#include <cstdlib>
#include <cstring>

#include <vector>

#include <unistd.h>

// The actual signal handler
extern "C" void handleSignal(int sig, siginfo_t *siginfo, void *) {
  // Cannot use printf() - not async-signal-safe 
  // For simplicity I use a single call to write here
  // though it is not guaranteed to write the whole message
  // You need to wrap it in a loop

  // Die only on Ctrl+C
  if(sig == SIGINT) {
    const char *msg = "Die\n";
    write(STDERR_FILENO, msg, ::strlen(msg));
    // Reset to use the default handler to do proper clean-up
    // If you want to call the default handler for every singal
    // You can avoid the call below by adding SA_RESETHAND to sa_flags
    signal(sig, SIG_DFL);
    raise(sig);
    return;
  }

  // Here we want to handle the signal ourselves
  // We have all the info available
  const char *msg = "Continue\n";
  write(STDERR_FILENO, msg, ::strlen(msg));
}

int main() {
  // You might want to setup your own alternative stack
  // eg. to handle SIGSEGV correctly
  // sigaltstack() + SA_ONSTACK flag in sa_flag

  // Prepare a signal action for handling any signal
  struct sigaction signal_action;

  signal_action.sa_sigaction = ::handleSignal;
  signal_action.sa_flags = SA_SIGINFO;
  ::sigfillset(&signal_action.sa_mask);
  // A vector of all signals that lead to process termination by default
  // (see man -s 7 signal)
  const int TERM_SIGNALS[] = {
        SIGHUP,  SIGINT,  SIGQUIT, SIGILL,    SIGABRT, SIGFPE, SIGSEGV,
        SIGPIPE, SIGALRM, SIGTERM, SIGUSR1,   SIGUSR2, SIGBUS, SIGPOLL,
        SIGPROF, SIGSYS,  SIGTRAP, SIGVTALRM, SIGXCPU, SIGXFSZ};

  // Register the signal event handler for every terminating signal
  for (auto sig : TERM_SIGNALS) {
    ::sigaction(sig, &signal_action, 0);
  }

  while(true);

  return 0;
}

1443720cookie-checkKann exit() den Prozess nicht beenden?

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

Privacy policy