<HTML> <HEAD> <TITLE>Réglages de haute précision</TITLE> </HEAD> <BODY> <H1>2. <A NAME="s2"></A>Réglages de haute précision</H1> <P> <A HREF="IO-Port.html#toc2">Contenu de cette section</A></P> <P></P> <H2>2.1 <A NAME="ss2.1"></A> Temporisations</H2> <P></P> <P>Tout d'abord, je dois préciser que, du fait de la nature multi-tâches préemptive de Linux, on ne peut pas garantir à un programme en mode utilisateur un contrôle exact du temps. Votre processus peut perdre l'usage du processeur à n'importe quel instant pour une période allant d'environ 20 millisecondes à quelques secondes (sur un système lourdement chargé). Néanmoins, pour la plupart des applications utilisant les ports d'E/S, cela ne pose pas de problèmes. Pour minimiser cet inconvénient, vous pouvez augmenter la priorité (avec <B>nice</B>) de votre programme.</P> <P>Il y a eu des discussions sur des projets de noyaux Linux temps-réel prenant ce phénomène en compte dans <EM>comp.os.linux.development.system</EM>, mais j'ignore leur avancement ; renseignez-vous dans ce groupe de discussion. Si vous en savez davantage, envoyez-moi un message...</P> <P>Maintenant, commençons par le plus facile. Pour des délais de plusieurs secondes, la meilleure fonction reste probablement <B>sleep(3)</B>. Pour des attentes de quelques dixièmes de secondes (20 ms semble un minimum), <B>usleep(3)</B> devrait convenir. Ces fonctions rendent le processeur aux autres processus, ce qui ne gâche pas de temps machine. Consultez les pages des manuels pour les détails.</P> <P>Pour des temporisations inférieures à 20 millisecondes environ (suivant la vitesse de votre processeur et de votre machine, ainsi que la charge du système), il faut proscrire l'abandon du processeur car l'ordonnanceur de Linux ne rendrait le contrôle à votre processus qu'après 20 millisecondes minimum (en général). De ce fait, pour des temporisations courtes, <B>usleep(3)</B> attendra souvent sensiblement plus longtemps que ce que vous avez spécifié, au moins 20 ms.</P> <P>Pour les délais courts (de quelques dizaines de microsecondes à quelques millisecondes), la méthode la plus simple consiste à utiliser <B>udelay()</B>, définie dans <B>/usr/include/asm/delay.h</B> (<B>linux/include/asm-i386/delay.h</B>). <B>udelay()</B> prend comme unique argument le nombre de microsecondes à attendre (unsigned long) et ne renvoie rien. L'attente dure quelques microsecondes de plus que le paramètre spécifié à cause du temps de calcul de la durée d'attente (voyez <B>delay.h</B> pour les détails).</P> <P>Pour utiliser <B>udelay()</B> en dehors du noyau, la variable (unsigned long) <B>loops_per_sec</B> doit être être définie avec la bonne valeur. Autant que je sache, la seule façon de récupérer cette valeur depuis le noyau consiste à lire le nombre de BogoMips dans <B>/proc/cpuinfo</B> puis à le multiplier par 500000. On obtient ainsi une évaluation (imprécise) de <B>loops_per_sec</B>.</P> <P>Pour les temporisations encore plus courtes, il existe plusieurs solutions. Ecrire n'importe quel octet sur le port 0x80 (voyez plus haut la manière de procéder) doit provoquer une attente d'exactement 1 microseconde, quelque soit le type et la vitesse de votre processeur. Cette écriture ne devrait pas avoir d'effets secondaires sur une machine standard (et certains pilotes de périphériques du noyau l'utilisent). C'est ainsi que <B>{in|out}{b|w}_p()</B> réalise normalement sa temporisation (voyez <B>asm/io.h</B>).</P> <P>Si vous connaissez le type de processeur et la vitesse de l'horloge de la machine sur laquelle votre programme tournera, vous pouvez coder des délais plus courts "en dur" en exécutant certaines instructions d'assembleur (mais souvenez-vous que votre processus peut perdre le processeur à tout instant, et, par conséquent, que l'attente peut, de temps à autres, s'avérer beaucoup plus importante). Dans la table suivante, la durée d'un cycle d'horloge est déterminée par la vitesse interne du processeur~; par exemple, pour un processeur à 50MHz (486DX-50 ou 486DX2-50), un cycle prend 1/50000000 seconde.</P> <P> <BLOCKQUOTE><CODE> <PRE> Instruction cycles sur i386 cycles sur i486 nop 3 1 xchg %ax,%ax 3 3 or %ax,%ax 2 1 mov %ax,%ax 2 1 add %ax,0 2 1 {source : Borland Turbo Assembler 3.0 Quick Reference} </PRE> </CODE></BLOCKQUOTE> </P> <P>(désolé, je n'ai pas de valeurs pour les Pentiums~ ce sont probablement les mêmes que pour i486)</P> <P>(Je ne connais pas d'instruction qui n'utilise qu'un seul cycle sur i386)</P> <P></P> <P> Les instructions <B>nop</B> et <B>xchg</B> du tableau n'ont pas d'effets de bord. Les autres peuvent modifier le registre des indicateurs, mais cela ne devrait pas avoir de conséquences puisque <B>gcc</B> est sensé le détecter.</P> <P>Pour vous servir de cette astuce, appelez <B>asm("intruction");</B> dans votre programme. Pour "instruction", utilisez la même syntaxe que dans la table précédente ; pour avoir plusieurs instructions dans un même <B>asm()</B>, faites <B>asm("instruction; instruction; instruction");</B>. Comme <B>asm()</B> est traduit en langage d'assemblage "inline" par gcc, il n'y a pas de perte de temps consécutive à un éventuel appel de fonction.</P> <P>L'architecture des Intel x86 n'autorise pas de temporisations inférieures à un cycle d'horloge.</P> <P></P> <H2>2.2 <A NAME="ss2.2"></A> Chronométrages</H2> <P></P> <P>Pour des chronométrages à la seconde près, le plus simple consiste probablement à utiliser <B>time(2)</B>. Pour des temps plus fins, <B>gettimeofday(2)</B> fournit une précision d'une microseconde (voyez toutefois, plus haut, les remarques concernant l'ordonnancement).</P> <P>Si vous désirez que votre processus reçoive un signal après un certain laps de temps, utilisez <B>setitimer(2)</B>. Consultez les pages des manuels des différentes fonctions pour les détails.</P> <P></P> <HR> <P> Chapitre <A HREF="IO-Port-3.html">suivant</A>, Chapitre <A HREF="IO-Port-1.html">Précédent</A> <P> Table des matières de <A HREF="IO-Port.html#toc2">ce chapitre</A>, <A HREF="IO-Port.html#toc">Table des matières</A> générale</P> <P> <A HREF="IO-Port.html">Début</A> du document, <A HREF="#0"> Début de ce chapitre</A></P> </BODY> </HTML>