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:
Das Verhalten von signal () variiert je nach UNIX-Version und hat sich auch historisch zwischen verschiedenen Linux-Versionen geändert. Vermeiden Sie die Verwendung: Verwenden Sie sigaction(2)
stattdessen. Siehe Portabilität unten.
Portabilität Die einzige tragbare Verwendung von signal () besteht darin, die Disposition eines Signals auf SIG_DFL oder SIG_IGN zu setzen. Die Semantik bei der Verwendung von signal () zum Einrichten eines Signalhandlers variiert systemübergreifend (und POSIX.1 erlaubt diese Variation ausdrücklich). Verwenden Sie es nicht für diesen Zweck.
Mit anderen Worten: nicht verwenden signal()
. Verwenden Sie sigaction()
stattdessen!
Was denkt GCC?
Kompatibilitätshinweis: Wie oben erwähnt signal
, sollte diese Funktion nach Möglichkeit vermieden werden. sigaction
ist die bevorzugte Methode.
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()
, sondern sigaction()
stattdessen zu verwenden , stellt sich die Frage: Wie zum Teufel verwenden wir diese verwirrende sigaction()
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-Handling
Und ihr AUSGEZEICHNETES sigaction()
Beispiel hier: https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html
Nachdem 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:
#include <errno.h> // errno
#include <signal.h> // sigaction()
#include <stdio.h> // printf()
#include <string.h> // strerror()
#define LOG_LOCATION __FILE__, __LINE__, __func__ // Format: const char *, unsigned int, const char *
#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;\n"
" 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 /programming/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 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:
#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 beachten sollten:
- Standardsignale: https://www.gnu.org/software/libc/manual/html_node/Standard-Signals.html#Standard-Signals
- Kündigungssignale: https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html#Termination-Signals
- Grundlegende Signalverarbeitung, einschließlich des offiziellen GCC-
signal()
Verwendungsbeispiels: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
- Offizielles GCC-
sigaction()
Verwendungsbeispiel: https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html
- 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:
- TutorialsPoint C ++ Signal Handling [mit ausgezeichnetem Demo-Code]: https://www.tutorialspoint.com/cplusplus/cpp_signal_handling.htm
- https://www.tutorialspoint.com/c_standard_library/signal_h.htm
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
.