<html><head><META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>4. Temporisation de haute précision</title><link href="style.css" rel="stylesheet" type="text/css"><meta content="DocBook XSL Stylesheets V1.69.1" name="generator"><link rel="start" href="index.html" title=" Petit guide de programmation des ports d'entrées / sorties sous Linux "><link rel="up" href="index.html" title=" Petit guide de programmation des ports d'entrées / sorties sous Linux "><link rel="prev" href="ar01s03.html" title="3. Interruptions (IRQ) et accès DMA"><link rel="next" href="ar01s05.html" title="5. D'autres langages de programmation"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table summary="Navigation header" width="100%"><tr><th align="center" colspan="3">4. Temporisation de haute précision</th></tr><tr><td align="left" width="20%"><a accesskey="p" href="ar01s03.html">Précédent</a> </td><th align="center" width="60%"> </th><td align="right" width="20%"> <a accesskey="n" href="ar01s05.html">Suivant</a></td></tr></table><hr></div><div class="sect1" lang="fr"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="N1023C"></a>4. Temporisation de haute précision</h2></div></div><div></div></div><div class="sect2" lang="fr"><div class="titlepage"><div><div><h3 class="title"><a name="N1023F"></a>4.1. Temporisations</h3></div></div><div></div></div><p> Avant toutes choses, il est important de préciser l'impossibilité de garantir un contrôle précis des temps d'exécution de processus en mode utilisateur du fait de la nature multitâche du noyau Linux. Votre processus peut être mis en sommeil à n'importe quel moment pour une durée allant de 10 millisecondes à quelques secondes (sur un système dont la charge est très importante). Malgré tout, pour la plupart des applications utilisant les ports d'entrées / sorties, cela n'est pas très important. Si vous voulez minimiser cet inconvénient, vous pouvez donner à votre processus une priorité plus haute (reportez-vous à la page de manuel de <span class="citerefentry"><span class="refentrytitle">nice</span>(2)</span>) ou faire appel à l'ordonnancement temps-réel (voir ci-après). </p><p> Si vous souhaitez obtenir une précision de temporisation plus élevée que celle qu'offre les processus en mode utilisateur usuels, sachez qu'il existe des possibilités de support « temps-réel » en mode utilisateur. Les noyaux Linux de la série 2.x permettent de travailler en quasi temps-réel. Pour les détails, reportez-vous à la page de manuel de <span class="citerefentry"><span class="refentrytitle">sched_setscheduler</span>(2)</span>. Il existe également des versions spéciales du noyau offrant un vrai ordonnancement temps-réel. </p><div class="sect3" lang="fr"><div class="titlepage"><div><div><h4 class="title"><a name="N10254"></a>4.1.1. Avec <code class="function">sleep()</code> et <code class="function">usleep()</code> </h4></div></div><div></div></div><p> Commençons tout d'abord par les appels de temporisation les plus simples. Pour des temporisation de plusieurs secondes, le meilleur choix est probablement la fonction <code class="function">sleep()</code>. Pour des durées au minimum de l'ordre de dizaines de millisecondes (10 millisecondes semblent être la durée minimum), <code class="function">usleep()</code> devrait s'avérer suffisant. Ces fonctions libèrent l'accès au microprocesseur pour d'autres processus, évitant ainsi le gaspillage du temps machine. Les pages de manuel de <span class="citerefentry"><span class="refentrytitle">sleep</span>(3)</span> et <span class="citerefentry"><span class="refentrytitle">usleep</span>(3)</span> vous donneront plus de précisions. </p><p> Pour des temporisations de moins de 50 millisecondes (en fonction de la cadence du microprocesseur, de la machine ainsi que de la charge du système), redonner le processeur aux autres processus prend énormément de temps. En effet l'ordonnanceur des tâches du noyau Linux (en tout cas pour les microprocesseurs de la famille x86) prend généralement au moins 10 à 30 millisecondes avant de rendre le contrôle au processus. De ce fait, dans les temporisations de courte durée, <span class="citerefentry"><span class="refentrytitle">usleep</span>(3)</span> effectue en réalité une pause plus longue que celle spécifiée en paramètre, prenant au moins 10 millisecondes supplémentaires. </p></div><div class="sect3" lang="fr"><div class="titlepage"><div><div><h4 class="title"><a name="N1027D"></a>4.1.2. <code class="function">nanosleep()</code></h4></div></div><div></div></div><p> Dans les noyaux Linux de la série 2.0.x est apparu l'appel système <code class="function">nanosleep()</code> (voir la page de manuel de <span class="citerefentry"><span class="refentrytitle">nanosleep</span>(2)</span>) permettant d'endormir ou de retarder un processus pendant un laps de temps très court (quelques microsecondes ou plus). </p><p> Pour des attentes ≤ 2 millisecondes, si (et seulement si) votre processus fonctionne en ordonnancement quasi temps-réel (au moyen de <code class="function">sched_setscheduler()</code>), <code class="function">nanosleep()</code> fait appel à une boucle d'attente ; si tel n'est pas le cas, le processus s'endort simplement tout comme avec <code class="function">usleep()</code>. </p><p> La boucle d'attente utilise <code class="function">udelay()</code> (une fonction interne au noyau utilisée par beaucoup de pilotes), la durée de celle-ci étant calculée en fonction du nombre de <code class="literal">BogoMips</code>. La vitesse de ce type de boucle d'attente est une des grandeurs que les <code class="literal">BogoMips</code> permettent de mesurer de façon précise. Voyez <code class="filename">/usr/include/asm/delay.h</code> pour plus de détails quant à son fonctionnement. </p></div><div class="sect3" lang="fr"><div class="titlepage"><div><div><h4 class="title"><a name="N102AF"></a>4.1.3. Temporisations grâce aux ports d'entrée / sortie</h4></div></div><div></div></div><p> Les accès aux ports d'entrée / sortie sont un autre moyen d'obtenir des temporisations. L'écriture ou la lecture d'un octet sur le port <code class="literal">0x80</code> (voir ci-dessus la procédure à suivre) devrait avoir pour conséquence un retard d'une microseconde, indépendamment du type et de la cadence du microprocesseur. Vous pouvez donc procéder de la sorte afin d'obtenir un retard de quelques microsecondes. L'écriture sur ce port ne devrait pas avoir d'effets secondaires sur une machine classique, pour preuve certains pilotes du noyau font appel à cette méthode. C'est également de cette manière que <code class="function">{in|out}[bw]_p()</code> effectue une pause (voir <code class="filename">asm/io.h</code>). </p><p> Plus précisément, une opération de lecture ou d'écriture sur la plupart des ports dans l'intervalle <code class="literal">0x000</code>-<code class="literal">0x3ff</code> prend 1 microseconde. Par exemple, si vous utilisez directement le port parallèle, il suffit d'utiliser des <code class="function">inb()</code> additionnels sur ce port pour obtenir une temporisation. </p></div><div class="sect3" lang="fr"><div class="titlepage"><div><div><h4 class="title"><a name="N102CF"></a>4.1.4. Temporisations en assembleur</h4></div></div><div></div></div><p> Si vous connaissez le type et la fréquence du processeur de la machine sur laquelle votre programme va s'exécuter, vous pouvez coder en dur les temporisations en faisant appel à certaines instructions assembleur. Rappelez-vous cependant qu'à tout moment votre processus peut-être mis en attente par l'ordonnanceur et, de ce fait, les temporisations peuvent s'avérer plus longues que souhaitées. </p><p> Pour les données du tableau ci-dessous, la fréquence interne du microprocesseur détermine le nombre de cycles d'horloge consommés. Par exemple, pour un microprocesseur à 50 Mhz (un 486DX-50), un cycle d'horloge dure 1/50000000 de seconde (soit 200 nanosecondes). </p><div class="informaltable"><table width="100%" border="1"><colgroup><col width="33%"><col width="33%"><col width="34%"></colgroup><thead><tr><th align="center">Instruction</th><th align="center">cycles d'horloge i386</th><th align="center">cycles d'horloge i486</th></tr></thead><tbody><tr><td align="center"><code class="literal">xchg %bx,%bx</code></td><td align="center">3</td><td align="center">3</td></tr><tr><td align="center"><code class="literal">nop</code></td><td align="center">3</td><td align="center">1</td></tr><tr><td align="center"><code class="literal">or %ax,%ax</code></td><td align="center">2</td><td align="center">1</td></tr><tr><td align="center"><code class="literal">mov %ax,%ax</code></td><td align="center">2</td><td align="center">1</td></tr><tr><td align="center"><code class="literal">add %ax,0</code></td><td align="center">2</td><td align="center">1</td></tr></tbody></table></div><p> Les cycles d'horloges du Pentium devraient être les mêmes que ceux du 486, à l'exception du Pentium Pro / II, dont l'instruction <code class="literal">add %ax, 0</code> peut ne consommer qu'un demi cycle d'horloge. Cette instruction peut-être parfois être combinée avec une autre (cependant, du fait de l'algorithme d'exécution hors de séquence (<span class="foreignphrase"><em class="foreignphrase">out-of-order</em></span>), il n'est pas nécessaire qu'il s'agisse d'une instruction consécutive dans le flot). </p><p> Les instructions <code class="literal">nop</code> et <code class="literal">xchg</code> du tableau ne devraient pas avoir d'effets secondaires. Les autres, en revanche, peuvent modifier le registre d'état, mais cela reste sans gravité puisque gcc devrait le détecter. <code class="literal">xchg %bx,%bx</code> reste un bon compromis comme instruction de temporisation. </p><p> Pour utiliser ces instructions, placez un appel <code class="function">asm("<em class="replaceable"><code>instruction</code></em>")</code> dans votre programme. La syntaxe d'appel de ces instructions est telle qu'énumérée dans le tableau ci-dessus. Si vous préférez grouper plusieurs instructions dans le même appel à <code class="function">asm</code>, il vous suffit de les séparer par des points-virgules. Par exemple <code class="function">asm("nop ; nop ; nop ; nop")</code> exécute quatre fois l'instruction <code class="literal">nop</code>, effectuant une temporisation de quatre cycles d'horloge sur un i486 ou un Pentium (ou douze cycles sur un i386). </p><p> Les instructions <code class="function">asm()</code> sont directement intégrées au code par gcc, évitant ainsi la perte de temps que pourrait engendrer un appel de fonction classique. </p><p> Les temporisations de moins d'un cycle d'horloge sont impossibles sur les architectures x86 d'Intel. </p></div><div class="sect3" lang="fr"><div class="titlepage"><div><div><h4 class="title"><a name="N10345"></a>4.1.5. <code class="literal">rdtsc</code> pour Pentium</h4></div></div><div></div></div><p> Avec les microprocesseurs Pentium, vous avez la possibilité de connaître le nombre de cycles d'horloge écoulés depuis le dernier redémarrage avec le code C suivant (qui fait appel à l'instruction appelée RDTSC) : </p><pre class="programlisting"> extern __inline__ unsigned long long int rdtsc() { unsigned long long int x; __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); return x; } </pre><p> Vous pouvez scruter cette valeur dans une boucle d'attente afin d'obtenir un retard correspondant au nombre de cycles d'horloge que vous souhaitez. </p></div></div><div class="sect2" lang="fr"><div class="titlepage"><div><div><h3 class="title"><a name="N10352"></a>4.2. Mesure du temps</h3></div></div><div></div></div><p> Pour des durées de la précision d'une seconde, il est certainement plus simple d'utiliser la fonction <code class="function">time()</code>. Pour obtenir plus de précision, <code class="function">gettimeofday()</code> a une précision d'environ une microseconde (mais rappelez-vous de l'ordonnancement déjà évoqué précédemment). Pour les Pentium, le fragment de code <code class="literal">rdtsc</code> ci-dessus est précis au cycle d'horloge près. </p><p> Si vous souhaitez que votre processus reçoive un signal après un certain laps de temps, utilisez <code class="function">setitimer()</code> ou <code class="function">alarm()</code>. Voyez les pages de manuel de ces fonctions pour plus de détails. </p></div></div><div class="navfooter"><hr><table summary="Navigation footer" width="100%"><tr><td align="left" width="40%"><a accesskey="p" href="ar01s03.html">Précédent</a> </td><td align="center" width="20%"> </td><td align="right" width="40%"> <a accesskey="n" href="ar01s05.html">Suivant</a></td></tr><tr><td valign="top" align="left" width="40%">3. Interruptions (IRQ) et accès DMA </td><td align="center" width="20%"><a accesskey="h" href="index.html">Sommaire</a></td><td valign="top" align="right" width="40%"> 5. D'autres langages de programmation</td></tr></table></div></body></html>