Was ist der Unterschied zwischen Signal und Signal?

Lesezeit: 12 Minuten

Benutzeravatar von Matthew Smith
Matthäus Smith

Ich wollte gerade einen zusätzlichen Signal-Handler zu einer App hinzufügen, die wir hier haben, und mir ist aufgefallen, dass der Autor ihn verwendet hat sigaction() um die anderen Signalhandler einzurichten. Ich wollte verwenden signal(). Um der Konvention zu folgen, sollte ich verwenden sigaction() aber wenn ich von Grund auf neu schreiben würde, was sollte ich wählen?

Benutzeravatar von Jonathan Leffler
Jonathan Leffler

Verwenden sigaction() es sei denn, Sie haben zwingende Gründe, dies nicht zu tun.

Das signal() Die Schnittstelle hat die Antike (und damit die Verfügbarkeit) zu ihren Gunsten und ist im C-Standard definiert. Trotzdem hat es eine Reihe unerwünschter Eigenschaften sigaction() vermeidet – es sei denn, Sie verwenden die explizit hinzugefügten Flags sigaction() um es zu ermöglichen, das Alte originalgetreu zu simulieren signal() Verhalten.

  1. Das signal() Die Funktion blockiert nicht (notwendigerweise) das Eintreffen anderer Signale, während der aktuelle Handler ausgeführt wird. sigaction() kann andere Signale blockieren, bis der aktuelle Handler zurückkehrt.
  2. Das signal() Funktion (normalerweise) setzt die Signalaktion zurück auf SIG_DFL (Standard) für fast alle Signale. Dies bedeutet, dass die signal() Handler muss sich als erste Aktion selbst neu installieren. Es öffnet auch ein Sicherheitsfenster zwischen dem Zeitpunkt, an dem das Signal erkannt wird, und der Neuinstallation des Handlers, in dem beim Eintreffen einer zweiten Instanz des Signals das Standardverhalten (normalerweise beenden, manchmal mit Vorurteilen – auch bekannt als Core-Dump) auftritt.
  3. Das genaue Verhalten von signal() variiert zwischen den Systemen – und die Standards erlauben diese Variationen.

Dies sind im Allgemeinen gute Gründe für die Verwendung sigaction() Anstatt von signal(). Allerdings ist die Schnittstelle von sigaction() ist unbestreitbar fummeliger.

Welche der beiden Sie auch verwenden, lassen Sie sich nicht von den alternativen Signalschnittstellen wie z
sighold(),
sigignore(),
sigpause() und
sigrelse(). Sie sind nominell Alternativen zu sigaction(), aber sie sind nur knapp standardisiert und in POSIX eher aus Gründen der Abwärtskompatibilität als für den ernsthaften Gebrauch vorhanden. Beachten Sie, dass der POSIX-Standard besagt, dass ihr Verhalten in Multithread-Programmen undefiniert ist.

Multithreaded-Programme und -Signale sind eine ganz andere komplizierte Geschichte. AFAIK, beides signal() und sigaction() sind in Multithread-Anwendungen in Ordnung.

Cornstalks beobachtet:

Die Linux-Manpage für signal() sagt:

  Die Effekte von signal() in einem Multithread-Prozess sind nicht spezifiziert.

So denke ich sigaction() ist die einzige, die sicher in einem Multithread-Prozess verwendet werden kann.

Das ist interessant. Die Linux-Handbuchseite ist in diesem Fall restriktiver als POSIX. POSIX spezifiziert für signal():

Wenn der Prozess Multithreading ist oder wenn der Prozess Singlethreading ist und ein Signalhandler ausgeführt wird, außer als Ergebnis von:

  • Der Prozess ruft abort(), raise(), kill(), pthread_kill()oder sigqueue() um ein nicht blockiertes Signal zu erzeugen
  • Ein anstehendes Signal wird entsperrt und geliefert, bevor der Anruf, der es entsperrt hat, zurückkehrt

das Verhalten ist undefiniert, wenn sich der Signal-Handler auf ein anderes Objekt als bezieht errno mit statischer Speicherdauer anders als durch Zuweisen eines Werts zu einem als deklarierten Objekt volatile sig_atomic_toder wenn der Signal-Handler eine andere in diesem Standard definierte Funktion als eine der in aufgelisteten Funktionen aufruft Signalkonzepte.

POSIX spezifiziert also eindeutig das Verhalten von signal() in einer Multithread-Anwendung.

Nichtsdestotrotz, sigaction() ist unter praktisch allen Umständen vorzuziehen – und portabler Multithread-Code sollte verwendet werden sigaction() es sei denn, es gibt einen zwingenden Grund, warum dies nicht möglich ist (z. B. „nur Funktionen verwenden, die von Standard C definiert sind“ – und ja, C11-Code kann Multithreading sein). Was im Grunde auch der einleitende Absatz dieser Antwort sagt.

  • Diese Beschreibung von signal ist eigentlich das Verhalten des Unix System V. POSIX erlaubt entweder dieses Verhalten oder das viel vernünftigere BSD-Verhalten, aber da Sie nicht sicher sein können, welches Sie bekommen, ist es immer noch am besten, es zu verwenden sigaction.

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

    15. August 2011 um 4:08 Uhr

  • es sei denn, Sie verwenden die explizit zu sigaction() hinzugefügten Flags, damit es das alte Signal()-Verhalten originalgetreu simulieren kann. Welche Flaggen wären das (konkret)?

    – Christian Cuevas

    30. Oktober 2014 um 1:20 Uhr


  • @AlexFritz: In erster Linie SA_RESETHANDAuch SA_NODEFER.

    – Jonathan Leffler

    30. Oktober 2014 um 1:28 Uhr

  • @BulatM. Wenn Sie nicht verwenden können sigaction()dann sind Sie im Wesentlichen verpflichtet, die Standard-C-Spezifikation für zu verwenden signal(). Das gibt Ihnen jedoch eine extrem verarmte Reihe von Optionen für das, was Sie tun können. Sie können: Variablen des Typs (Dateibereich) ändern volatile sig_atomic_t; Rufen Sie eine der ‘Quick Exit’-Funktionen auf (_Exit(), quick_exit()) oder abort(); Anruf signal() mit der aktuellen Signalnummer als Signalargument; Rückkehr. Und das ist es. Alles andere ist nicht garantiert tragbar. Das ist so streng, dass die meisten Leute diese Regeln ignorieren – aber der resultierende Code ist zwielichtig.

    – Jonathan Leffler

    27. Oktober 2016 um 16:14 Uhr

  • Exzellent sigaction() Demo von GCC selbst: gnu.org/software/libc/manual/html_node/…; und ausgezeichnet signal() Demo von GCC selbst: gnu.org/software/libc/manual/html_node/…. Beachten Sie, dass in der signal Demo vermeiden sie es, den Handler von Ignorieren (SIG_IGN), wenn es zuvor absichtlich so eingestellt war.

    – Gabriel Staples

    12. November 2019 um 3:21 Uhr


Benutzeravatar von Gabriel Staples
Gabriel Staples

Zusamenfassend:

sigaction() (sehen hier und hier) ist gut und wohldefiniert, aber eine POSIX-Funktion und funktioniert daher nur auf Linux- oder POSIX-Systemen. signal() (sehen hier und hier) ist schlecht und schlecht definiert, aber eine C-Standardfunktion und funktioniert daher mit allem.

Was sagen die Linux Manpages dazu?

man 2 signal (sehen Sie es hier online) Zustände:

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

Portabilität

Die einzige tragbare Verwendung von signal() ist es, eine Signaldisposition zu setzen
SIG_DFL oder SIG_IGN. Die Semantik bei der Verwendung signal() um einen systemübergreifenden Signal-Handler einzurichten (und POSIX.1 erlaubt diese Variation ausdrücklich); verwenden Sie es nicht für diesen Zweck.

Mit anderen Worten: tun nicht verwenden signal(). Verwenden sigaction() stattdessen!

Diese Position wird in der nächsten Zeile wiederholt, in der es heißt (Hervorhebung hinzugefügt):

POSIX.1 löste das Problem der Portabilität, indem es spezifizierte sigaction(2)das eine explizite Kontrolle der Semantik bereitstellt, wenn ein Signalhandler aufgerufen wird; Verwenden Sie stattdessen diese Schnittstelle signal().

Was denkt GCC?

Aus https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling (Betonung hinzugefügt):

Kompatibilitätshinweis: Wie oben gesagt für signal, Diese Funktion sollte nach Möglichkeit vermieden werden. sigaction ist die bevorzugte Methode.

Also, wenn sowohl Linux als auch GCC sagen, nicht zu verwenden signal()aber zu verwenden sigaction() Stattdessen stellt sich die Frage: Wie zum Teufel verwenden wir diese Verwirrung sigaction() Ding!?

Anwendungsbeispiele:

Lesen Sie GCCs AUSGEZEICHNET signal() Beispiel hier: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling

Und ihre AUSGEZEICHNETEN sigaction() Beispiel hier: https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html

Nachdem ich diese Seiten gelesen hatte, kam ich auf die folgende Technik für sigaction():

1. sigaction()da es der richtige Weg ist, einen Signal-Handler wie oben beschrieben anzuhängen:

#include <errno.h>  // errno
#include <signal.h> // sigaction()
#include <stdio.h>  // printf()
#include <string.h> // strerror()

// Format: const char *, unsigned int, const char *
#define LOG_LOCATION __FILE__, __LINE__, __func__ 
#define LOG_FORMAT_STR "file: %s, line: %u, func: %s: "

/// @brief      Callback function to handle termination signals, such as 
///             Ctrl + C
/// @param[in]  signal  Signal number of the signal being handled by this 
///             callback function
/// @return     None
static void termination_handler(const int signal)
{
    switch (signal)
    {
    case SIGINT:
        printf("\nSIGINT (%i) (Ctrl + C) signal caught.\n", signal);
        break;
    case SIGTERM:
        printf("\nSIGTERM (%i) (default `kill` or `killall`) signal caught.\n", 
            signal);
        break;
    case SIGHUP:
        printf("\nSIGHUP (%i) (\"hang-up\") signal caught.\n", signal);
        break;
    default:
        printf("\nUnk signal (%i) caught.\n", signal);
        break;
    }

    // DO PROGRAM CLEANUP HERE, such as freeing memory, closing files, etc.

    
    exit(signal);
}

/// @brief      Set a new signal handler action for a given signal
/// @details    Only update the signals with our custom handler if they are NOT
///     set to "signal ignore" (`SIG_IGN`), which means they are currently
///     intentionally ignored. GCC recommends this "because non-job-control
///     shells often ignore certain signals when starting children, and it is
///     important for children to respect this." See
///     https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
///     and
///     https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html.
///     Note that termination signals can be found here:
///     https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html#Termination-Signals
/// @param[in]  signal  Signal to set to this action
/// @param[in]  action  Pointer to sigaction struct, including the callback 
///     function inside it, to attach to this signal
/// @return     None
static inline void set_sigaction(int signal, const struct sigaction *action)
{
    struct sigaction old_action;

    // check current signal handler action to see if it's set to SIGNAL IGNORE
    sigaction(signal, NULL, &old_action);
    if (old_action.sa_handler != SIG_IGN)
    {
        // set new signal handler action to what we want
        int ret_code = sigaction(signal, action, NULL);
        if (ret_code == -1)
        {
            printf(LOG_FORMAT_STR "sigaction failed when setting signal to "
                   "%i; errno = %i: %s\n", 
                   LOG_LOCATION, signal, errno, strerror(errno));
        }
    }
}

int main(int argc, char *argv[])
{
    //...

    // Register callbacks to handle kill signals; prefer the Linux function
    // `sigaction()` over the C function `signal()`: "It is better to use
    // sigaction if it is available since the results are much more reliable."
    // Source:
    // https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
    // and
    // https://stackoverflow.com/questions/231912/what-is-the-difference-between-sigaction-and-signal/232711#232711.
    // See here for official gcc `sigaction()` demo, which this code is modeled
    // after:
    // https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html

    // Set up the structure to specify the new action, per GCC's demo.
    struct sigaction new_action;
    new_action.sa_handler = termination_handler; // set callback function
    sigemptyset(&new_action.sa_mask);
    new_action.sa_flags = 0;

    // SIGINT: ie: Ctrl + C kill signal
    set_sigaction(SIGINT, &new_action);
    // SIGTERM: termination signal--the default generated by `kill` and
    // `killall`
    set_sigaction(SIGTERM, &new_action);
    // SIGHUP: "hang-up" signal due to lost connection
    set_sigaction(SIGHUP, &new_action);

    //...
}

2. Und für signal()auch wenn es keine gute Möglichkeit ist, einen Signal-Handler wie oben beschrieben anzuhängen, ist es dennoch gut zu wissen, wie man ihn benutzt.

Hier ist der GCC-Demonstrationscode, der kopiert und eingefügt wurde, da er so gut wie möglich ist:

#include <signal.h>

void
termination_handler (int signum)
{
  struct temp_file *p;

  for (p = temp_file_list; p; p = p->next)
    unlink (p->name);
}

int
main (void)
{
  …
  if (signal (SIGINT, termination_handler) == SIG_IGN)
    signal (SIGINT, SIG_IGN);
  if (signal (SIGHUP, termination_handler) == SIG_IGN)
    signal (SIGHUP, SIG_IGN);
  if (signal (SIGTERM, termination_handler) == SIG_IGN)
    signal (SIGTERM, SIG_IGN);
  …
}

Die wichtigsten Links, die Sie kennen sollten:

  1. Standardsignale: https://www.gnu.org/software/libc/manual/html_node/Standard-Signals.html#Standard-Signals
  2. Beendigungssignale: https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html#Termination-Signals
  3. Grundlegende Signalbehandlung, einschließlich offizieller GCC signal() Anwendungsbeispiel: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
  4. Offizieller GCC sigaction() Anwendungsbeispiel: https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html
  5. Signalsätze, einschließlich sigemptyset() und sigfillset(); Ich verstehe diese immer noch nicht genau, weiß aber, dass sie wichtig sind: https://www.gnu.org/software/libc/manual/html_node/Signal-Sets.html

Siehe auch:

  1. Meine Antwort auf “Wie man manuell ein beliebiges Signal an einen laufenden Prozess sendet” und “Wie man ein beliebiges Signal in Ihrem Programm abfängt” (z. B. in Bash).
  2. TutorialsPoint C++-Signalverarbeitung [with excellent demo code]: https://www.tutorialspoint.com/cplusplus/cpp_signal_handling.htm
  3. https://www.tutorialspoint.com/c_standard_library/signal_h.htm

  • Ich verstehe nicht sigemptyset entweder

    – jcarlosweb

    4. September um 13:37 Uhr

Für mich war diese folgende Zeile genug, um zu entscheiden:

Die Funktion sigaction() bietet einen umfassenderen und zuverlässigeren Mechanismus zum Steuern von Signalen; neue Anwendungen sollten sigaction() statt signal() verwenden

http://pubs.opengroup.org/onlinepubs/009695399/functions/signal.html#tag_03_690_07

Egal, ob Sie bei Null anfangen oder ein altes Programm modifizieren, sigaction sollte die richtige Option sein.

Sie sind verschiedene Schnittstellen für die Signaleinrichtungen von OS. Man sollte es vorziehen, wenn möglich sigaction zum Signalisieren zu verwenden, da signal() ein implementierungsdefiniertes (oft Race-anfälliges) Verhalten hat und sich auf Windows-, OS X-, Linux- und anderen UNIX-Systemen anders verhält.

Sieh dir das an Sicherheitshinweis für Details.

signal() ist Standard-C, sigaction() nicht.

Wenn Sie beides verwenden können (das heißt, Sie befinden sich auf einem POSIX-System), verwenden Sie sigaction(); Es ist nicht angegeben, ob signal() den Handler zurücksetzt, was bedeutet, dass Sie signal() innerhalb des Handlers erneut aufrufen müssen, um portierbar zu sein. Was noch schlimmer ist, ist, dass es ein Rennen gibt: Wenn Sie zwei Signale kurz hintereinander erhalten und das zweite geliefert wird, bevor Sie den Handler neu installieren, haben Sie die Standardaktion, die wahrscheinlich darin besteht, Ihren Prozess zu beenden.
sigaction(), hingegen verwendet garantiert eine „zuverlässige“ Signalsemantik. Sie müssen den Handler nicht neu installieren, da er nie zurückgesetzt wird. Mit SA_RESTART können Sie auch einige Systemaufrufe automatisch neu starten lassen (damit Sie nicht manuell nach EINTR suchen müssen).
sigaction() hat mehr Optionen und ist zuverlässig, daher wird seine Verwendung empfohlen.

Psst … erzählen Sie niemandem, dass ich Ihnen das gesagt habe, aber POSIX hat derzeit eine Funktion bsd_signal(), die sich wie signal() verhält, aber BSD-Semantik gibt, was bedeutet, dass sie zuverlässig ist. Seine Hauptverwendung ist das Portieren alter Anwendungen, die von zuverlässigen Signalen ausgegangen sind, und POSIX rät davon ab, es zu verwenden.

  • POSIX hat keine Funktion bsd_signal() — Einige POSIX-Implementierungen können die Funktion haben, aber POSIX selbst enthält keine solche Funktion (siehe Posix).

    – Jonathan Leffler

    13. August 2019 um 16:22 Uhr

Benutzeravatar von Greg Hewgill
Greg Hewgill

Von dem signal(3) Manpage:

BEZEICHNUNG

 This signal() facility is a simplified interface to the more
 general sigaction(2) facility.

Beide rufen dieselbe zugrunde liegende Einrichtung auf. Sie sollten vermutlich nicht die Antwort auf ein einzelnes Signal mit beiden manipulieren, aber das Mischen sollte nicht dazu führen, dass etwas kaputt geht …

  • POSIX hat keine Funktion bsd_signal() — Einige POSIX-Implementierungen können die Funktion haben, aber POSIX selbst enthält keine solche Funktion (siehe Posix).

    – Jonathan Leffler

    13. August 2019 um 16:22 Uhr

Benutzeravatar von pradyumna kaushik
pradyumna kaushik

Ich würde auch vorschlagen, sigaction() über signal() zu verwenden und möchte einen weiteren Punkt hinzufügen. sigaction() gibt Ihnen mehr Optionen wie die PID des Prozesses, der gestorben ist (möglich mit der Struktur siginfo_t).

1424380cookie-checkWas ist der Unterschied zwischen Signal und Signal?

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

Privacy policy