Verwenden sigaction()
Sie es, es sei denn, Sie haben sehr zwingende Gründe, dies nicht zu tun.
Die signal()
Schnittstelle hat die Antike (und damit die Verfügbarkeit) zu ihren Gunsten und ist im C-Standard definiert. Trotzdem weist es eine Reihe unerwünschter Eigenschaften auf, die sigaction()
vermieden werden - es sei denn, Sie verwenden die explizit hinzugefügten Flags sigaction()
, um das alte signal()
Verhalten originalgetreu zu simulieren .
- Die
signal()
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.
- Die
signal()
Funktion setzt (normalerweise) die Signalaktion SIG_DFL
für fast alle Signale auf (Standard) zurück. Dies bedeutet, dass sich der signal()
Handler als erste Aktion neu installieren muss. Es öffnet sich auch ein Fenster der Sicherheitsanfälligkeit zwischen dem Zeitpunkt der Erkennung des Signals und der Neuinstallation des Handlers. Wenn eine zweite Instanz des Signals eintrifft, tritt das Standardverhalten (normalerweise beendet, manchmal mit Vorurteilen - auch bekannt als Core Dump) auf.
- Das genaue Verhalten
signal()
variiert zwischen den Systemen - und die Standards erlauben diese Variationen.
Dies sind im Allgemeinen gute Gründe für die Verwendung sigaction()
anstelle von signal()
. Die Schnittstelle von sigaction()
ist jedoch zweifellos fummeliger.
Unabhängig davon , welche der beiden Sie verwenden, nicht durch die alternativen Signalschnittstellen wie versucht sein
sighold()
,
sigignore()
,
sigpause()
und
sigrelse()
. Sie sind nominell Alternativen zu sigaction()
, aber sie sind kaum standardisiert und in POSIX aus Gründen der Abwärtskompatibilität und nicht für den ernsthaften Gebrauch vorhanden. Beachten Sie, dass der POSIX-Standard besagt, dass ihr Verhalten in Multithread-Programmen undefiniert ist.
Multithread-Programme und -Signale sind eine ganz andere komplizierte Geschichte. AFAIK, beide signal()
und sigaction()
sind in Multithread-Anwendungen in Ordnung.
Cornstalks beobachtet :
Die Linux-Manpage für signal()
sagt:
Die Auswirkungen signal()
eines Multithread-Prozesses sind nicht spezifiziert.
Daher denke ich, dass dies sigaction()
das einzige ist, das 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 Single-Threaded ist und ein Signalhandler ausgeführt wird, der nicht das Ergebnis von:
- Der Prozess calling
abort()
, raise()
, kill()
, pthread_kill()
, oder sigqueue()
ein Signal zu erzeugen , das nicht blockiert ist
- Ein anstehendes Signal, das entsperrt und vor dem nicht gesperrten Anruf zugestellt wird, kehrt zurück
Das Verhalten ist undefiniert, wenn der Signalhandler auf ein anderes Objekt als errno
mit einer anderen statischen Speicherdauer verweist, als indem er einem als deklarierten Objekt einen Wert zuweist volatile sig_atomic_t
, oder wenn der Signalhandler eine in diesem Standard definierte Funktion außer einer der in aufgeführten Funktionen aufruft Signalkonzepte .
Daher legt POSIX das Verhalten signal()
in einer Multithread-Anwendung klar fest .
Trotzdem sigaction()
ist es im Wesentlichen unter allen Umständen vorzuziehen - und tragbarer Multithread-Code sollte verwendet werden, es sigaction()
sei denn, es gibt einen überwältigenden Grund, warum dies nicht möglich ist (z. B. "nur Funktionen verwenden, die durch Standard C definiert sind" - und ja, C11-Code kann Multi sein -gewinde). Welches ist im Grunde, was der erste Absatz dieser Antwort auch sagt.
signal
bezieht sich tatsächlich auf das Verhalten von Unix System V. POSIX erlaubt entweder dieses Verhalten oder das viel vernünftigere BSD-Verhalten, aber da Sie nicht sicher sind, welches Sie erhalten, ist es immer noch am besten, es zu verwendensigaction
.SA_RESETHAND
auchSA_NODEFER
.sigaction()
, müssen Sie im Wesentlichen die Standard C-Spezifikation für verwendensignal()
. Dies gibt Ihnen jedoch eine äußerst verarmte Auswahl an Möglichkeiten für das, was Sie tun können. Sie können: Variablen vom Typ (Dateibereich) ändernvolatile sig_atomic_t
; Rufen Sie eine der 'Quick Exit'-Funktionen auf (_Exit()
,quick_exit()
) oderabort()
; Anrufsignal()
mit der aktuellen Signalnummer als Signalargument; Rückkehr. Und das ist es. Alles andere ist garantiert nicht tragbar. Das ist so streng, dass die meisten Leute diese Regeln ignorieren - aber der resultierende Code ist zweifelhaft.sigaction()
Demo von GCC selbst: gnu.org/software/libc/manual/html_node/… ; und eine exzellentesignal()
Demo von GCC selbst: gnu.org/software/libc/manual/html_node/… . Beachten Sie, dass in dersignal
Demo vermieden wird, dass der Handler von ignore (SIG_IGN
) geändert wird, wenn dies zuvor absichtlich festgelegt wurde.Für mich war diese Zeile ausreichend, um zu entscheiden:
http://pubs.opengroup.org/onlinepubs/009695399/functions/signal.html#tag_03_690_07
Unabhängig davon, ob Sie bei Null anfangen oder ein altes Programm ändern, sollte Sigaction die richtige Option sein.
quelle
Sie sind verschiedene Schnittstellen für die Signalfunktionen des Betriebssystems. Man sollte es vorziehen, Sigaction zu verwenden, um wenn möglich zu signalisieren, da signal () ein implementierungsdefiniertes (häufig rennanfälliges) Verhalten aufweist und sich unter Windows, OS X, Linux und anderen UNIX-Systemen anders verhält.
Weitere Informationen finden Sie in diesem Sicherheitshinweis .
quelle
signal () ist Standard C, sigaction () nicht.
Wenn Sie beides verwenden können (dh Sie befinden sich auf einem POSIX-System), verwenden Sie sigaction (). Es ist nicht angegeben, ob signal () den Handler zurücksetzt. Um portabel zu sein, müssen Sie signal () im Handler erneut aufrufen. Schlimmer ist, dass es ein Rennen gibt: Wenn Sie zwei Signale schnell hintereinander erhalten und das zweite vor der Neuinstallation des Handlers übermittelt wird, haben Sie die Standardaktion, die wahrscheinlich darin besteht, Ihren Prozess abzubrechen. sigaction () empfohlen hingegen verwendet garantiert eine „zuverlässige“ Signalsemantik. Sie müssen den Handler nicht neu installieren, da er niemals zurückgesetzt wird. Mit SA_RESTART können Sie auch einige Systemaufrufe zum automatischen Neustart veranlassen (sodass Sie nicht manuell nach EINTR suchen müssen). Sigaction () hat mehr Optionen und ist zuverlässig, so dass seine Verwendung empfohlen wird.
Psst ... sag niemandem, dass ich dir das gesagt habe, aber POSIX hat derzeit eine Funktion bsd_signal (), die sich wie signal () verhält, aber BSD-Semantik gibt, was bedeutet, dass es zuverlässig ist. Die Hauptanwendung ist die Portierung alter Anwendungen, bei denen zuverlässige Signale angenommen wurden, und POSIX empfiehlt die Verwendung nicht.
quelle
bsd_signal()
- einige POSIX-Implementierungen haben möglicherweise die Funktion, aber POSIX selbst enthält keine solche Funktion (siehe POSIX ).Zusamenfassend:
sigaction()
ist gut und gut definiert, ist aber eine Linux-Funktion und funktioniert daher nur unter Linux.signal()
ist schlecht und schlecht definiert, ist aber eine C-Standardfunktion und funktioniert daher bei allem.Was sagen die Linux-Manpages dazu?
man 2 signal
(siehe hier online ) heißt es:Mit anderen Worten: nicht verwenden
signal()
. Verwenden Siesigaction()
stattdessen!Was denkt GCC?
Quelle: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
Wenn also sowohl Linux als auch GCC sagen, nicht zu verwenden
signal()
, sondernsigaction()
stattdessen zu verwenden , stellt sich die Frage: Wie zum Teufel verwenden wir diese verwirrendesigaction()
Sache?Anwendungsbeispiele:
Lesen Sie hier das AUSGEZEICHNETE
signal()
Beispiel von GCC : https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-HandlingUnd ihr AUSGEZEICHNETES
sigaction()
Beispiel hier: https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.htmlNachdem ich diese Seiten gelesen hatte, entwickelte ich die folgende Technik für
sigaction()
:1.
sigaction()
, da es der richtige Weg ist, einen Signalhandler wie oben beschrieben anzuschließen:2. Und
signal()
obwohl es keine gute Möglichkeit ist, einen Signalhandler wie oben beschrieben anzuschließen, ist es dennoch gut zu wissen, wie man ihn verwendet.Hier ist der GCC-Demonstrationscode, der kopiert wurde, da er ungefähr so gut ist, wie er nur sein wird:
Die wichtigsten Links, die Sie beachten sollten:
signal()
Verwendungsbeispiels: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handlingsigaction()
Verwendungsbeispiel: https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.htmlsigemptyset()
undsigfillset()
; Ich verstehe diese immer noch nicht genau, weiß aber, dass sie wichtig sind: https://www.gnu.org/software/libc/manual/html_node/Signal-Sets.htmlSiehe auch:
quelle
Von der
signal(3)
Manpage:Beide rufen dieselbe zugrunde liegende Fazilität auf. Sie sollten vermutlich nicht die Antwort eines einzelnen Signals mit beiden manipulieren, aber das Mischen sollte nicht dazu führen, dass etwas kaputt geht ...
quelle
Ich würde auch vorschlagen, sigaction () über signal () zu verwenden und möchte noch einen Punkt hinzufügen. sigaction () bietet Ihnen weitere Optionen, z. B. die PID des abgelaufenen Prozesses (möglich mit der Struktur siginfo_t).
quelle
Ich würde signal () verwenden, da es zumindest theoretisch portabler ist. Ich werde jeden Kommentator abstimmen, der ein modernes System entwickeln kann, das keine POSIX-Kompatibilitätsschicht hat und signal () unterstützt.
Zitat aus der GLIBC-Dokumentation :
quelle
Vom Manpage- Signal (7)
Und ich würde sagen, dass dieses "Problem" für Signal (2) und Sigaktion (2) besteht . Seien Sie also vorsichtig mit Signalen und Pthreads.
... und Signal (2) scheint unter Linux mit glibc Sigaction (2) aufzurufen .
quelle