La réponse courte à votre question est oui.Ils fonctionnent même si le processus auquel ils sont rattachés est occupé à faire des calculs ou à attendre quelque chose. La seule limitation est que le processus ne change pas et joue avec SIGALRM
lui-même ce qui causerait des problèmes. Lorsqu'un signal arrive, l'exécution normale d'un processus/thread est suspendue et le gestionnaire de signal est appelé. C'est l'idée de la gestion des signaux et pourquoi elle est appelée asynchronous
.
La réponse la plus longue à votre question est non. Vous ne voudriez pas implémenter de rapport de progression via le mécanisme du gestionnaire de signaux car l'API que vous pouvez utiliser dans un gestionnaire de signal est très limitée. Pour être exact l'exemple que vous avez mentionné mal, car il utilise fprintf(3)
. Comme indiqué dans l'une des réponses, les signaux sont asynchrones. Cela signifie que si le signal arrive au milieu du code principal appelant, malloc(3)
, et votre code appelle malloc(3)
ainsi (vous ne pouvez jamais savoir, printf(3)
peut appeler malloc(3)
pour la mise en mémoire tampon et d'autres besoins) alors vous corrompt les données internes des mallocs structures et provoquer le programme de faute. Vous pouvez même avoir des problèmes pour appeler vos propres fonctions qui ne sont pas asynchrones. Vous avez une liste de fonctions sûres que vous pouvez appeler à l'intérieur d'un gestionnaire de signal et vous pouvez les trouver dans man 7 signal
sous Async-signal-safe functions
. Donc oui, techniquement, vous pouvez implémenter des rapports de progression via alarm(3)
tant que vous êtes prêt à vivre avec cette API réduite et que c'est la raison pour laquelle je ne le ferais pas à moins que le programme ne soit unique par conception et à moins qu'il n'y ait vraiment aucun moyen Je vois le code de rapport d'avancement comme étant sujet à de futures améliorations qui rendront difficile d'écrire dans un gestionnaire de signal.
Un autre problème qui a été indiqué à propos de votre exemple est que alarm(2)
n'accepte pas les arguments inférieurs à la seconde et l'exemple ci-dessus aurait dû échouer complètement ou afficher au moins quelques avertissements à ce sujet.
Pour la résolution de la microseconde, vous pouvez utiliser setitimer(2)
avec ITIMER_REAL
comme cela a été indiqué.
Pour la résolution de nanoseconde sur Linux, vous pouvez utiliser timer_create(2)
, CLOCK_REALTIME
, SIGEV_SIGNAL
et timer_settime(2)
qui ont beaucoup plus de fonctionnalités.
Voici un exemple de code. Notez que cela utilise mes propres macros de gestion des erreurs. Vous pouvez le voir dans un état compilable dans ce projet demos-linux
#include <signal.h> // for signal(2), SIG_ERR
#include <unistd.h> // for alarm(2), write(2)
#include <stdlib.h> // for EXIT_SUCCESS
#include <err_utils.h> // for CHECK_NOT_M1(), CHECK_NOT_SIGT()
#include <stdio.h> // for snprintf(3), STDERR_FILENO
/*
* This is an example of doing progress reports via the SIGALRM signal every second.
* The main code does a tight calculation loop and the progress reporting to stderr
* (file descriptor 2) is done via the alarm signal handler.
*/
/*
* The variables are global to allow the signal handler to access them easily
* You DONT need to initialize them to 0 since that is the default.
* The 'volatile' on i is *critical* since it will be accessed asynchronously
* and the compiler needs to know not to put it in a register since that
* will mean that we cannot report it's value correctly from the signal
* handler.
*/
volatile unsigned long i;
/*
* Remember that this is a signal handler and calls to fprintf(3) or the like
* are forbidden so we are forced to use async-safe function (see man 7 signal).
* That is the reason for the cumbersome code. Hopefully snprintf(3) is safe enough
* to use.
*/
static void handler(int sig) {
// we have to reschedule the SIGALRM every time since the alarm(2)
// is a one time deal.
CHECK_NOT_SIGT(signal(SIGALRM, handler), SIG_ERR);
// no error code from alarm(2)
alarm(1);
char buf[100];
int len=snprintf(buf, sizeof(buf), "did [%ld] units of work...\n", i);
CHECK_NOT_M1(write(STDERR_FILENO, buf, len));
}
int main(int argc, char** argv, char** envp) {
CHECK_NOT_SIGT(signal(SIGALRM, handler), SIG_ERR);
// no error code from alarm(2)
alarm(1);
// a very long calculation
while(true) {
/* Do some real work here */
i++;
}
return EXIT_SUCCESS;
}
Comment puis-je faire accepter 500ms, par exemple? –
Utilisez 'setitimer' avec' ITIMER_REAL'. Recherchez des exemples utilisant cette fonction. – nategoose