Dois-je utiliser un gestionnaire de signal pour une timer Posix?

Je veux démarrer une timer et avoir une fonction appelée quand elle expire.

Googling trouve de nombreux exemples, y compris celui du manuel, qui utilisent tous sigaction() pour définir un gestionnaire de signal.

Cependant, @Patryk dit dans cette question que nous pouvons simplement

 void cbf(union sigval); struct sigevent sev; timer_t timer; sev.sigev_notify = SIGEV_THREAD; sev.sigev_notify_function = cbf; //this function will be called when timer expires sev.sigev_value.sival_ptr = (void*) arg;//this argument will be passed to cbf timer_create(CLOCK_MONOTONIC, &sev, &timer); 

ce qui est plus court, plus simple, plus propre, plus maintenable …

Ce qui donne? Est-ce correct? Est-ce juste un wrapper pour sigaction() ? Pourquoi les exemples définissent-ils explicitement un gestionnaire de signal?

De plus, si je démarre une timer par cette méthode ou par timer_settime et un gestionnaire de signal, l’annulation de la timer demandera au système de supprimer l’association entre cette timer et le rappel, ou dois-je le faire explicitement?


[Mise à jour] Vous pouvez choisir les signaux ou la méthode indiquée dans ma réponse ci-dessous (ou les deux, mais cela semble idiot). C’est une question de goût. Les chants pourraient offrir un peu plus de fonctionnalité, au désortingment de la complaisance.

Si tout ce que vous voulez faire est de démarrer une timer et d’être averti de son expiration, la méthode décrite dans ma réponse est la plus simple.

Linux a timerfd. https://lwn.net/Articles/251413/ . Cela permet d’utiliser un temps d’attente avec select / poll / epoll. Sinon, vous pouvez utiliser le délai d’expiration sur select / poll / epoll.

Michael Kerrisk a un exemple détaillé dans son livre “The Linux Programming Interface”:

 /* ptmr_sigev_thread.c This program demonstrates the use of threads as the notification mechanism for expirations of a POSIX timer. Each of the program's command-line arguments specifies the initial value and interval for a POSIX timer. The format of these arguments is defined by the function itimerspecFromStr(). The program creates and arms one timer for each command-line argument. The timer notification method is specified as SIGEV_THREAD, causing the timer notifications to be delivered via a thread that invokes threadFunc() as its start function. The threadFunc() function displays information about the timer expiration, increments a global counter of timer expirations, and signals a condition variable to indicate that the counter has changed. In the main thread, a loop waits on the condition variable, and each time the condition variable is signaled, the main thread prints the value of the global variable that counts timer expirations. Kernel support for Linux timers is provided since Linux 2.6. On older systems, an incomplete user-space implementation of POSIX timers was provided in glibc. */ #include  #include  #include  #include "curr_time.h" /* Declares currTime() */ #include "tlpi_hdr.h" #include "itimerspec_from_str.h" /* Declares itimerspecFromStr() */ static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; static int expireCnt = 0; /* Number of expirations of all timers */ static void /* Thread notification function */ threadFunc(union sigval sv) { timer_t *tidptr; int s; tidptr = sv.sival_ptr; printf("[%s] Thread notify\n", currTime("%T")); printf(" timer ID=%ld\n", (long) *tidptr); printf(" timer_getoverrun()=%d\n", timer_getoverrun(*tidptr)); /* Increment counter variable shared with main thread and signal condition variable to notify main thread of the change. */ s = pthread_mutex_lock(&mtx); if (s != 0) errExitEN(s, "pthread_mutex_lock"); expireCnt += 1 + timer_getoverrun(*tidptr); s = pthread_mutex_unlock(&mtx); if (s != 0) errExitEN(s, "pthread_mutex_unlock"); s = pthread_cond_signal(&cond); if (s != 0) errExitEN(s, "pthread_cond_signal"); } int main(int argc, char *argv[]) { struct sigevent sev; struct itimerspec ts; timer_t *tidlist; int s, j; if (argc < 2) usageErr("%s secs[/nsecs][:int-secs[/int-nsecs]]...\n", argv[0]); tidlist = calloc(argc - 1, sizeof(timer_t)); if (tidlist == NULL) errExit("malloc"); sev.sigev_notify = SIGEV_THREAD; /* Notify via thread */ sev.sigev_notify_function = threadFunc; /* Thread start function */ sev.sigev_notify_attributes = NULL; /* Could be pointer to pthread_attr_t structure */ /* Create and start one timer for each command-line argument */ for (j = 0; j < argc - 1; j++) { itimerspecFromStr(argv[j + 1], &ts); sev.sigev_value.sival_ptr = &tidlist[j]; /* Passed as argument to threadFunc() */ if (timer_create(CLOCK_REALTIME, &sev, &tidlist[j]) == -1) errExit("timer_create"); printf("Timer ID: %ld (%s)\n", (long) tidlist[j], argv[j + 1]); if (timer_settime(tidlist[j], 0, &ts, NULL) == -1) errExit("timer_settime"); } /* The main thread waits on a condition variable that is signaled on each invocation of the thread notification function. We print a message so that the user can see that this occurred. */ s = pthread_mutex_lock(&mtx); if (s != 0) errExitEN(s, "pthread_mutex_lock"); for (;;) { s = pthread_cond_wait(&cond, &mtx); if (s != 0) errExitEN(s, "pthread_cond_wait"); printf("main(): expireCnt = %d\n", expireCnt); } } 

Tiré du code source en ligne .

Lisez également le chapitre 23 du livre, ce code y est expliqué en détail.

Pour tester le code ci-dessus, on entrerait

 $ ./ptmr_sigev_thread 5:5 10:10 

Cela définira deux temporisateurs: l'un avec une expiration initiale de 5 secondes et un intervalle avec 5 secondes, et l'autre avec 10 respectivement.

Les définitions des fonctions d'assistance peuvent être trouvées en suivant le lien sur le code source du livre ci-dessus.

Il semble que je n’ai pas besoin d’utiliser un gestionnaire de signal et que je puisse simplifier le code, comme illustré ci-dessous:

 #include  #include  #include  #include  #include  static unsigned int pass_value_by_pointer = 42; void Timer_has_expired(union sigval timer_data) { printf("Timer expiration handler function; %d\n", *(int *) timer_data.sival_ptr); } int main(void) { struct sigevent timer_signal_event; timer_t timer; struct itimerspec timer_period; printf("Create timer\n"); timer_signal_event.sigev_notify = SIGEV_THREAD; timer_signal_event.sigev_notify_function = Timer_has_expired; // This function will be called when timer expires // Note that the following is a union. Assign one or the other (preferably by pointer) //timer_signal_event.sigev_value.sival_int = 38; // This argument will be passed to the function timer_signal_event.sigev_value.sival_ptr = (void *) &pass_value_by_pointer; // as will this (both in a structure) timer_signal_event.sigev_notify_atsortingbutes = NULL; timer_create(CLOCK_MONOTONIC, &timer_signal_event, &timer); printf("Start timer\n"); timer_period.it_value.tv_sec = 1; // 1 second timer timer_period.it_value.tv_nsec = 0; // no nano-seconds timer_period.it_interval.tv_sec = 0; // non-repeating timer timer_period.it_interval.tv_nsec = 0; timer_settime(timer, 0, &timer_period, NULL); sleep(2); printf("----------------------------\n"); printf("Start timer a second time\n"); timer_settime(timer, 0, &timer_period, NULL); sleep(2); printf("----------------------------\n"); printf("Start timer a third time\n"); timer_settime(timer, 0, &timer_period, NULL); printf("Cancel timer\n"); timer_delete(timer); sleep(2); printf("The timer expiration handler function should not have been called\n"); return EXIT_SUCCESS; } 

lorsqu’il est exécuté, il donne cette sortie:

 Create timer Start timer Timer expiration handler function; 42 ---------------------------- Start timer a second time Timer expiration handler function; 42 ---------------------------- Start timer a third time Cancel timer The timer expiration handler function should not have been called