timerfd_create(2) System Calls Manual timerfd_create(2)

timerfd_create, timerfd_settime, timerfd_gettime - Minuteries qui informent par l'intermédiaire de descripteurs de fichier

Bibliothèque C standard (libc, -lc)

#include <sys/timerfd.h>
int timerfd_create(int clockid, int flags);
int timerfd_settime(int fd, int flags,
                    const struct itimerspec *new_value,
                    struct itimerspec *_Nullable old_value);
int timerfd_gettime(int fd, struct itimerspec *curr_value);

Ces appels système créent et opèrent sur une minuterie qui fournit des notifications d'expiration par un descripteur de fichier. Ils fournissent une alternative à setitimer(2) ou timer_create(2) avec l'avantage que le descripteur de fichier peut être surveillé avec select(2), poll(2) ou epoll(7).

L'utilisation de ces trois appels système est analogue à l'utilisation de timer_create(2), timer_settime(2) et timer_gettime(2). (Il n'y a pas d'équivalent à timer_getoverrun(2) puisque cette fonctionnalité est fournie par read(2), comme décrit ci-dessous)

timerfd_create() crée un nouvel objet minuterie et renvoie un descripteur de fichier qui se réfère à cette minuterie. Le paramètre clockid indique l'horloge utilisée pour marquer la progression de la minuterie qui doit être une des suivantes :

Une horloge temps réel configurable à l'échelle du système.
Une horloge non configurable, toujours croissante qui mesure le temps depuis un instant non spécifié dans le passé et qui ne change pas après le démarrage du système.
C'est une horloge toujours croissante comme CLOCK_MONOTONIC. Cependant alors que l'horloge CLOCK_MONOTONIC ne mesure pas le temps aussi longtemps que le système est suspendu, l'horloge CLOCK_BOOTTIME inclut le temps pendant lequel le système est suspendu. Cela est utile pour les applications qui doivent être sensibles au temps de suspension. CLOCK_REALTIME n'est pas adapté à ce type d'application dans la mesure où cette horloge est affectée par des modifications discontinues de l'horloge système.
Cette horloge se comporte comme CLOCK_REALTIME, mais réveillera le système s'il est suspendu. L'appelant doit avoir la capacité CAP_WAKE_ALARM afin de régler une minuterie utilisant cette horloge.
Cette horloge se comporte comme CLOCK_BOOTTIME, mais réveillera le système s'il est suspendu. L'appelant doit avoir la capacité CAP_WAKE_ALARM afin de régler une minuterie utilisant cette horloge.

Consultez clock_getres(2) pour quelques détails supplémentaires sur les horloges mentionnées.

La valeur actuelle de chacune de ces horloges peut être obtenue avec clock_gettime(2).

À partir de Linux 2.6.27, les valeurs suivantes peuvent être incluses avec un OU binaire dans flags pour changer le comportement de timerfd_create() :

Placer l'attribut d'état de fichier O_NONBLOCK sur la description du fichier ouvert référencée par le nouveau descripteur de fichier (consulter open(2)). Utiliser cet attribut économise des appels supplémentaires à fcntl(2) pour obtenir le même résultat.
Placer l'attribut « close-on-exec » (FD_CLOEXEC) sur le nouveau descripteur de fichier. Consultez la description de l'attribut O_CLOEXEC dans open(2) pour savoir pourquoi cela peut être utile.

Dans les versions de Linux jusqu'à la version 2.6.26 incluse, flags doit être nul.

timerfd_settime() arme (démarre) ou désarme (stoppe) la minuterie à laquelle se réfère le descripteur de fichier fd.

Le paramètre new_value spécifie l'expiration initiale et l'intervalle de la minuterie. La structure itimerspec utilisée pour ce paramètre est décrite dans itimerspec(3type) :

new_value.it_value spécifie l'expiration initiale de la minuterie, en secondes et nanosecondes. Une valeur non nulle dans un des champs de new_value.it_value arme la minuterie. La minuterie est désarmée si les deux champs de new_value.it_value sont mis à zéro.

Une valeur non nulle dans un des champs de new_value.it_interval configure la période, en secondes et nanosecondes, pour une expiration répétitive après l'expiration initiale. Si les deux champs de new_value.it_interval sont nuls, la minuterie expirera qu'une seule fois, dont l'heure est spécifiée dans new_value.it_value.

Par défaut, l'heure d'expiration initiale spécifiée dans new_value est interprétée de façon relative par rapport à l'heure actuelle sur l'horloge de la minuterie au moment de l'appel (c'est-à-dire que new_value.it_value indique une heure relative à la valeur actuelle de l'horloge spécifiée par clockid). Un délai absolu peut être sélectionné avec le paramètre flags.

Le paramètre flags est un masque de bits qui peut avoir les valeurs suivantes :

Interpréter new_value.it_value comme une valeur absolue sur l'horloge de la minuterie. La minuterie expirera quand la valeur de l'horloge de la minuterie atteint la valeur spécifiée dans new_value.it_value.
Si cet attribut est spécifié en même temps que TFD_TIMER_ABSTIME et si l'horloge pour cette minuterie est CLOCK_REALTIME ou CLOCK_REALTIME_ALARM, alors marquer cette minuterie comme annulable si l'horloge en temps réel subit une modification discontinue (settimeofday(2), clock_settime(2) ou similaire). Quand des modifications se produisent, un appel actuel ou futur à read(2) à partir du descripteur de fichier échouera avec l’erreur ECANCELED.

Si le paramètre old_value n'est pas égal à NULL, la structure itimerspec vers laquelle il pointe est utilisée pour renvoyer la configuration de la minuterie au moment de l'appel ; consultez la description de timerfd_gettime() ci-dessous.

timerfd_gettime() renvoie, dans curr_value, une structure itimerspec qui contient les paramètres actuels de la minuterie auquel le descripteur de fichier fd fait référence.

Le champ it_value renvoie la durée jusqu'à la prochaine expiration. Si les deux champs de cette structure sont nuls, alors la minuterie est actuellement désactivée. Ce champ contient toujours une valeur relative, sans tenir compte d'un attribut TFD_TIMER_ABSTIME qui aurait été spécifié quand la minuterie a été configurée.

Le champ it_interval renvoie l'intervalle de la minuterie. Si les deux champs de cette structure sont nuls, alors la minuteries est configurée pour n'expirer qu'une seule fois, à l'heure spécifiée par curr_value.it_value.

Le descripteur de fichier renvoyé par timerfd_create() gère les opérations supplémentaires suivantes :

read(2)
Si la minuterie a déjà expirée une fois ou plus depuis que sa configuration a été modifiée la dernière fois à l'aide de timerfd_settime() ou depuis la dernière lecture avec read(2) qui a réussi, alors le tampon fourni à read(2) renvoie un entier non signé sur 8 octets (uint64_t) qui contient le nombre d'expirations qui se sont produites. (La valeur renvoyée utilise l'ordre des octets de l'hôte, c'est-à-dire l'ordre des octets natif pour les entiers sur la machine hôte.)
Si aucune expiration ne s'est produite au moment de l'appel à read(2), l'appel bloquera jusqu'à la prochaine expiration ou échouera avec l'erreur EAGAIN si le descripteur de fichier est en mode non bloquant (à l'aide de de l'opération F_SETFL de fcntl(2) pour régler l'attribut O_NONBLOCK).
Un read(2) échouera avec l'erreur EINVAL si la taille du tampon fourni est de moins de 8 octets.
Si l'horloge associée est soit CLOCK_REALTIME ou CLOCK_REALTIME_ALARM, si la minuterie est absolue (TFD_TIMER_ABSTIME) et si l'attribut TFD_TIMER_CANCEL_ON_SET a été spécifié lors de l'appel timerfd_settime(), alors l'appel à read(2) échoue avec l'erreur ECANCELED si l'horloge en temps réel subit une modification discontinue. (Cela permet à l'application qui lit de découvrir ce type de modifications discontinues à l'horloge.)
Si l'horloge associée est soit CLOCK_REALTIME ou CLOCK_REALTIME_ALARM, si la minuterie est absolue (TFD_TIMER_ABSTIME) et si l'attribut TFD_TIMER_CANCEL_ON_SET a été spécifié lors de l'appel timerfd_settime(), alors une modification négative discontinue à l'horloge (par exemple, clock_settime(2)) peut faire à que read(2) supprime le blocage, mais renvoie une valeur de 0 (c'est-à-dire qu'aucun octet n'est lu), si une modification d'horloge survient après que le temps soit expiré, mais avant le read(2) sur le descripteur de fichier.
poll(2)
select(2)
(et similaire)
Le descripteur de fichier est lisible (le paramètre readfds de select(2) ; l'attribut POLLIN de poll(2)) si une expiration (ou plus) de la minuterie s'est produite.
Le descripteur de fichier prend également en charge les autres interfaces de multiplexage de descripteurs de fichier : pselect(2), ppoll(2) et epoll(7).
ioctl(2)
La commande suivante spécifique à timerfd est prise en charge :
Ajuste le nombre d'expirations de minuterie qui sont survenues. Le paramètre est un pointeur vers un entier de 8 octets différent de zéro (uint64_t*) contenant le nouveau nombre d'expirations. Une fois que le nombre est défini, tout processus en attente de la minuterie est réveillé. Le seul objectif de cette commande est de rétablir les expirations dans l'objectif de points de vérification ou de restauration. Cette opération est disponible seulement si le noyau a été configuré avec l'option CONFIG_CHECKPOINT_RESTORE.
close(2)
Quand le descripteur de fichier n'est plus nécessaire il doit être fermé. Quand tous les descripteurs de fichier associés au même objet minuterie ont été fermés, la minuterie est désarmée et ses ressources sont libérées par le noyau.

Sémantique de fork(2)

Après un fork(2), l'enfant hérite d'une copie du descripteur de fichier créé par timerfd_create(). Le descripteur de fichier se réfère au même objet minuterie sous-jacent que le descripteur de fichier correspondant dans le parent, et un read(2) de l'enfant renverra les informations sur les expirations de la minuterie.

Sémantique de execve(2)

Un descripteur de fichier créé par timerfd_create() est conservé au travers d'un execve(2), et continue à générer des expirations de minuterie si la minuterie a été armée.

S'il réussit, timerfd_create() renvoie un nouveau descripteur de fichier. En cas d'erreur, il renvoie -1 et errno est défini pour indiquer l'erreur.

En cas de réussite, timerfd_settime() et timerfd_gettime() renvoient 0. Sinon ils renvoient -1 et définissent errno pour indiquer l'erreur.

timerfd_create() peut échouer avec les erreurs suivantes :

Le clockid n'est pas valable.
flags n'est pas correct ; ou, pour les versions de Linux 2.6.26 ou antérieures, flags n'est pas nul.
La limite du nombre de descripteurs de fichiers par processus a été atteinte.
La limite du nombre total de fichiers ouverts pour le système entier a été atteinte.
Impossible de monter (en interne) le périphérique anonyme d'inœud.
Pas assez de mémoire noyau pour créer la minuterie.
clockid était CLOCK_REALTIME_ALARM ou CLOCK_BOOTTIME_ALARM, mais l'appelant n'a pas la capacité CAP_WAKE_ALARM.

timerfd_settime() et timerfd_gettime() peuvent échouer avec les erreurs suivantes :

fd n'est pas un descripteur de fichier valable.
new_value, old_value ou curr_value n'est pas un pointeur valable.
fd n'est pas un descripteur de fichier de minuterie valable.

timerfd_settime() peut aussi échouer avec les erreurs suivantes :

Voir NOTES
new_value n'est pas initialisé correctement (un des champs tv_nsec est en dehors de l'intervalle allant de 0 à 999 999 999).
flags n'est pas correct.

Linux.

Linux 2.6.25, glibc 2.8.

En supposant le scénario suivant pour une minuterie CLOCK_REALTIME ou CLOCK_REALTIME_ALARM créée avec timerfd_create() :

(1)
la minuterie a été démarrée (timerfd_settime()) avec les attributs TFD_TIMER_ABSTIME et TFD_TIMER_CANCEL_ON_SET ;
(2)
une modification discontinue (par exemple, settimeofday(2)) est ensuite appliquée à l'horloge CLOCK_REALTIME ;
(3)
l'appelant appelle une fois de plus timerfd_settime() pour réarmer la minuterie (sans exécuter préalablement un read(2) sur le descripteur de fichier).

Dans ce cas les événements suivants se produisent :

  • timerfd_settime() renvoie -1 avec errno défini à ECANCELED. (Cela permet à l'appelant de savoir que la minuterie précédente a été affectée par une modification discontinue de l'horloge.)
  • La minuterie est réarmée avec succès avec les réglages fournis dans le second appel timerfd_settime(). (C'est probablement un accident d'implémentation, mais ne sera pas corrigé maintenant au cas où des applications dépendent de ce comportement.)

Actuellement, timerfd_create() prend en charge moins de types d'identifiant d'horloges que timer_create(2).

Le programme suivant crée une minuterie puis surveille sa progression. Le programme accepte jusqu'à trois paramètres en ligne de commande. Le premier paramètre spécifie le nombre de secondes pour l'expiration initiale de la minuterie. Le deuxième paramètre spécifie l'intervallse de la minuterie, en secondes. Le troisième paramètre spécifie le nombre de fois que le programme doit permettre à la minuterie d'expirer avant de quitter. Le deuxième et le troisième paramètre sont optionnels.

La session interactive suivante montre l'utilisation de ce programme :


$ a.out 3 1 100
0.000: timer started
3.000: read: 1; total=1
4.000: read: 1; total=2
^Z                 # entrer Ctrl-Z pour suspendre le programme
[1]+  Stopped                 ./timerfd3_demo 3 1 100
$ fg                # Reprendre l'exécution après quelques secondes
a.out 3 1 100
9.660: read: 5; total=7
10.000: read: 1; total=8
11.000: read: 1; total=9
^C                  # entrer Ctrl-C pour suspendre le programme

#include <err.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/timerfd.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
static void
print_elapsed_time(void)
{
    int                     secs, nsecs;
    static int              first_call = 1;
    struct timespec         curr;
    static struct timespec  start;
    if (first_call) {
        first_call = 0;
        if (clock_gettime(CLOCK_MONOTONIC, &start) == -1)
            err(EXIT_FAILURE, "clock_gettime");
    }
    if (clock_gettime(CLOCK_MONOTONIC, &curr) == -1)
        err(EXIT_FAILURE, "clock_gettime");
    secs = curr.tv_sec - start.tv_sec;
    nsecs = curr.tv_nsec - start.tv_nsec;
    if (nsecs < 0) {
        secs--;
        nsecs += 1000000000;
    }
    printf("%d.%03d: ", secs, (nsecs + 500000) / 1000000);
}
int
main(int argc, char *argv[])
{
    int                fd;
    ssize_t            s;
    uint64_t           exp, tot_exp, max_exp;
    struct timespec    now;
    struct itimerspec  new_value;
    if (argc != 2 && argc != 4) {
        fprintf(stderr, "%s init-secs [interval-secs max-exp]\n",
                argv[0]);
        exit(EXIT_FAILURE);
    }
    if (clock_gettime(CLOCK_REALTIME, &now) == -1)
        err(EXIT_FAILURE, "clock_gettime");
    /* Créer une minuterie absolue CLOCK_REALTIME avec une expiration
       et un intervalle initiaux comme spécifié en ligne de commande. */
    new_value.it_value.tv_sec = now.tv_sec + atoi(argv[1]);
    new_value.it_value.tv_nsec = now.tv_nsec;
    if (argc == 2) {
        new_value.it_interval.tv_sec = 0;
        max_exp = 1;
    } else {
        new_value.it_interval.tv_sec = atoi(argv[2]);
        max_exp = atoi(argv[3]);
    }
    new_value.it_interval.tv_nsec = 0;
    fd = timerfd_create(CLOCK_REALTIME, 0);
    if (fd == -1)
        err(EXIT_FAILURE, "timerfd_create");
    if (timerfd_settime(fd, TFD_TIMER_ABSTIME, &new_value, NULL) == -1)
        err(EXIT_FAILURE, "timerfd_settime");
    print_elapsed_time();
    printf("timer started\n");
    for (tot_exp = 0; tot_exp < max_exp;) {
        s = read(fd, &exp, sizeof(uint64_t));
        if (s != sizeof(uint64_t))
            err(EXIT_FAILURE, "read");
        tot_exp += exp;
        print_elapsed_time();
        printf("read: %" PRIu64 "; total=%" PRIu64 "\n", exp, tot_exp);
    }
    exit(EXIT_SUCCESS);
}

eventfd(2), poll(2), read(2), select(2), setitimer(2), signalfd(2), timer_create(2), timer_gettime(2), timer_settime(2), timespec(3), epoll(7), time(7)

La traduction française de cette page de manuel a été créée par Christophe Blaess https://www.blaess.fr/christophe/, Stéphan Rafin <stephan.rafin@laposte.net>, Thierry Vignaud <tvignaud@mandriva.com>, François Micaux, Alain Portal <aportal@univ-montp2.fr>, Jean-Philippe Guérard <fevrier@tigreraye.org>, Jean-Luc Coulon (f5ibh) <jean-luc.coulon@wanadoo.fr>, Julien Cristau <jcristau@debian.org>, Thomas Huriaux <thomas.huriaux@gmail.com>, Nicolas François <nicolas.francois@centraliens.net>, Florentin Duneau <fduneau@gmail.com>, Simon Paillard <simon.paillard@resel.enst-bretagne.fr>, Denis Barbier <barbier@debian.org>, David Prévot <david@tilapin.org>, Cédric Boutillier <cedric.boutillier@gmail.com>, Frédéric Hantrais <fhantrais@gmail.com> et Jean-Pierre Giraud <jean-pierregiraud@neuf.fr>

Cette traduction est une documentation libre ; veuillez vous reporter à la GNU General Public License version 3 concernant les conditions de copie et de distribution. Il n'y a aucune RESPONSABILITÉ LÉGALE.

Si vous découvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un message à debian-l10n-french@lists.debian.org.

2 mai 2024 Pages du manuel de Linux 6.8