Les timers sous Linux

Gazette Linux n°103 — Juin 2004

Nikhil Bhargava

Article paru dans le n°103 de la Gazette Linux de juin 2004.

Traduction française par Joëlle Cornavin .

Relecture de la traduction française par Encolpe Degoute .

Article publié sous Open Publication License. La Linux Gazette n'est ni produite, ni sponsorisée, ni avalisée par notre hébergeur principal, SSC, Inc.


Table des matières
1. Introduction
2. RTC et horloge système
3. Interruption du timer
4. jiffies
5. Méthodes propres à l'architecture
6. Heure actuelle
7. Timers du noyau

1. Introduction

Cet article est consacré aux timers que fournit le système d'exploitation Linux. Les timers sont vitaux à la fois pour les programmes de niveau applicatif aussi bien que pour les pilotes de périphériques et d'autres programmes système de niveau inférieur. Le système d'exploitation Linux fournit divers mécanismes pour offrir des timers dont quelques-uns seront décrits ici. La version de noyau auquel cet article fait réfrence est la version 2.4.20. Ce document vise à aider le développeur à analyser les possibilités de timerde Linux, mais aucun code d'implémentation n'a été prévu ici.


2. RTC et horloge système

Un des principaux domaines dans la gestion du temps qui est souvent une source de confusion est la différence entre l'horloge temps réel (Real Time Clock, RTC) et horloge système (system Clock. La RTC garde la trace du temps quand le système est arrêté à l'aide d'une petite batterie située sur la carte mère (habituellement la batterie CMOS dans les ordinateurs de bureau). Lors de l'amorçage, la RTC est lue par le noyau pour pouvoir initialiser une variable xtime (xtime est déclarée dans le noyau Linux 2.4 dans /include/linux/sched.h). Le noyau utilise cette variable pour initialiser ensuite l'horloge murale (time_of_the_day). Ainsi, les applications de l'espace utilisateur qui lisent l'heure du jour tirent en fait leur valeur de la variable xtime. D'ordinaire, puisque le noyau ne lit la RTC qu'à l'amorçage, le changement de la RTC n'est pas répercuté dans la méthode de l'espace utilisateur qui appelle time_of_the_day().


3. Interruption du timer

Le noyau garde généralement la trace des intervalles de temps (habituellement pour entretenir divers timers appelés par des applications) à l'aide d'une interruption de timer produite par le timer matériel du système à des périodes préétablies, qui est définie par une variable HZ. Cette variable est définie en fonction de la valeur de HZ (qui peut être fixée pendant la durée de construction du noyau), la valeur de timer minimale de base (fréquence de base d'interruption d'horloge) prise en charge par le système peut changer (la valeur par défaut est 10 millisecondes).


4. jiffies

jiffies est une variable système interne maintenue par le noyau, qui stocke le nombre de ticks d'horloge depuis la mise sous tension (démarrage) du système. Elle est définie dans <linux/schedule.h>. Cependant, bien qu'elle soit définie comme volatile et longue, elle peut déborder en raison d'un temps utilisable continu (environ un an et demi).


5. Méthodes propres à l'architecture

La plupart des plates-formes prennent en charge quelques mécanismes de timer propres à l'architecture, ce qui aide les développeurs à avoir des intervalles de timer très courts. Une telle option est un registre de compteur de timer qui incrémente sa valeur après chaque cycle d'horloge. Ce peut être 32 bits ou 64 bits, selon la plate-forme. Le registre compteur le plus actualisé est le TSC (TimeStamp Counter, compteur d'estampilles temporelles) qui est maintenant disponible sur toutes les plates-formes majeures et peut être lu à la fois depuis l'espace utilisateur et l'espace du noyau. Les macros suivantes sont employées pour lire ce registre (#include <asm/msr.h>)


rtdsc(low,high); // Capture la valeur sur 64 bits en deux variables 32 bits
rtdscl(low); // Capture la valeur 32 bits inférieure

La majorité des autres plates-formes (je ne suis pas sûr que toutes l'offrent) disposent d'une fonction indépendante de l'architecture en remplacement de rtdsc (#include <linux/timex.h>)


cycles_t get_cycles(void); // Capture seulement les 32 bits inférieurs du compteur

6. Heure actuelle

La valeur de jiffies peut toujours donner le module, la valeur correcte de l'heure actuelle. Les jiffies sont une excellente méthode pour mesurer des intervalles de timer relativement longs. Toutefois, il faut revenir aux méthodes propres à l'architecture pour obtenir des timers relativement courts. D'ordinaire, les programmes système comme les pilotes de périphériques ou les modules du noyau exigent l'horloge système et non l'horloge murale (le format du temps comme le donne un programme utilisateur comme cron et at). Il y a donc beaucoup de fonctions prévues pour savoir l'heure actuelle.

Une telle méthode est gettimeofday(). Elle remplit une structure timeval avec l'heure actuelle en une valeur en secondes et micro-secondes. Une autre fonction similaire est do_gettimeofday(). Son prototype est défini comme (#include <linux/time.h>).


void do_gettimeofday(struct timeval *tv); 

Cette fonction donne une valeur de résolution proche de la micro-seconde. Une autre option pour obtenir l'heure actuelle d'une manière rapide et efficace est la fonction du noyau get_fast_time () :


void get_fast_time (struct timeval *tv); 

De plus, le moyen le plus facile de lire l'heure actuelle est d'utiliser la valeur de la variable xtime, bien que son niveau de précision soit assez bas. Cependant, un accès à tous ses champs ne peut être possible atomiquement, à moins que les interrruptions soient désactivées, ce qui est vivement déconseillé dans le cas des Real Time Systems (systèmes temps réel).


7. Timers du noyau

Le timer est un mécanisme permettant d'assurer la fonctionnalité temporelle vitale dans tout module. Dans les cas où une tâche doit contrôler un tempons ou su un périphérique quelconque doit être surveillée après une période de temps donnée, qu'il soit périodique ou discret, il doit y avoir un mécanisme pour annoncer la tâche à la fin de l'intervalle donné, de façon à ce qu'elle soit libre de poursuivre son cours normal du travail sans déranger ou attendre la survenue de l'annonce. Le processus du timer est employé pour satisfaire à de tels besoins dans n'importe quelle application.

Les timers du noyau sont organisés en une liste doublement chaînée avec un pointeur à la fois avant et arrière en fonctionnement. Le timer est caractérisé par sa valeur après laquelle il devra expirer ; sa marque d'identification (nom, processus auquel il appartient et autres données) et la fonction appelée (handler) qui est invoquée quand le timer expire. La structure de données d'un timer ressemble à cela (#<linux/timer.h>) :


struct timer_list
{
        struct timer_list *next; // pointeur vers le prochain timer de la liste
        struct timer_list *prev; // pointeur sur le précédent timer de la liste
        unsigned long expires; // valeurs de temps imparti en jiffies
        unsigned long data; // argument pour le gestionnaire 
        void (*function) (unsigned long); // fonction du gestionnaire
        volatile int running;   // drapeau désignant l'état du nœud
};

Le gestionnaire du timer sera appelé quand timer->expires>=jiffies. Une fois que la structure timer_list est initialisée, add_timer() l'insère dans une liste triée qui est scrutée périodiquement. Voici toutes API (Application Programming Interfaces, interface de programmation d'applications) du noyau indiquées :


void init_timer (struct timer_list* timer);

Cette fonction initialise un timer nouvellement alloué.


void add_timer (struct timer_list* timer); 

Cette fonction insère le timer nouvellement allouée dans la liste globale des timers actifs.


void mod_timer (struct timer_list* timer, unsigned long expires);

Cette fonction sert à modifier la valeur d'un timer actif.


int del_timer (struct timer_list* timer);

Cette fonction sert à supprimer le timer spécifié de la liste des timers actifs.

Je m'appelle Nikhil Bhargava et je travaille actuellement sur les piles du protocole de communication mobile de troisième génération (3G) chez C-DOT, à Dehli (Inde) depuis un an et demi en tant qu'ingénieur de recherche et de développement (Research Engineer). Mes centres d'intérêt en matière de recherche comprennent les systèmes RTAI (Real Time Application Interface) et les réseaux mobiles (Mobile Networks). Tous commentaires, corrections de bogues et suggestions seront les bienvenus.