En bref:
sigaction()
est bon et bien défini, mais est une fonction Linux et ne fonctionne donc que sous Linux. signal()
est mauvaise et mal définie, mais est une fonction standard C et donc elle fonctionne sur n'importe quoi.
Que disent les pages de manuel Linux à ce sujet?
man 2 signal
(voir en ligne ici ) déclare:
Le comportement de signal () varie selon les versions UNIX et a également varié historiquement entre les différentes versions de Linux. Évitez son utilisation: utilisez sigaction(2)
plutôt. Voir la portabilité ci-dessous.
Portabilité La seule utilisation portable de signal () est de définir la disposition d'un signal sur SIG_DFL ou SIG_IGN. La sémantique lors de l'utilisation de signal () pour établir un gestionnaire de signaux varie d'un système à l'autre (et POSIX.1 autorise explicitement cette variation); ne l'utilisez pas à cette fin.
En d'autres termes: ne pas utiliser signal()
. Utilisez sigaction()
plutôt!
Que pense GCC?
Note de compatibilité: comme indiqué ci-dessus pour signal
, cette fonction doit être évitée lorsque cela est possible. sigaction
est la méthode préférée.
Source: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
Donc, si Linux et GCC disent de ne pas utiliser signal()
, mais d'utiliser à la sigaction()
place, cela soulève la question: comment diable utilisons-nous cette sigaction()
chose déroutante !?
Exemples d'utilisation:
Lisez EXCELLENT signal()
exemple de GCC ici: https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
Et leur EXCELLENT sigaction()
exemple ici: https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html
Après avoir lu ces pages, j'ai mis au point la technique suivante pour sigaction()
:
1. sigaction()
, car c'est la bonne façon d'attacher un gestionnaire de signaux, comme décrit ci-dessus:
#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. Et pour signal()
, même si ce n'est pas un bon moyen d'attacher un gestionnaire de signaux, comme décrit ci-dessus, il est toujours bon de savoir comment l'utiliser.
Voici le code de démonstration GCC copié, car il est à peu près aussi bon qu'il va l'être:
#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);
…
}
Les principaux liens à connaître:
- Signaux standard: https://www.gnu.org/software/libc/manual/html_node/Standard-Signals.html#Standard-Signals
- Signaux de terminaison: https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html#Termination-Signals
- Gestion de base du signal, y compris l'
signal()
exemple d'utilisation officiel de GCC : https://www.gnu.org/software/libc/manual/html_node/Basic-Signal-Handling.html#Basic-Signal-Handling
sigaction()
Exemple d'utilisation officiel de GCC : https://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html
- Ensembles de signaux, y compris
sigemptyset()
et sigfillset()
; Je ne les comprends toujours pas exactement, mais je sais qu'ils sont importants: https://www.gnu.org/software/libc/manual/html_node/Signal-Sets.html
Voir également:
- TutorialsPoint C ++ Signal Handling [avec un excellent code de démonstration]: https://www.tutorialspoint.com/cplusplus/cpp_signal_handling.htm
- https://www.tutorialspoint.com/c_standard_library/signal_h.htm
signal
est en fait le comportement d'Unix System V. POSIX autorise ce comportement ou le comportement BSD beaucoup plus sain, mais comme vous ne pouvez pas être sûr de celui que vous obtiendrez, il est toujours préférable de l'utilisersigaction
.