Sophie

Sophie

distrib > * > 2010.0 > * > by-pkgid > a412ceb851151854794ced2a242192bb > files > 1392

howto-html-fr-20080722-1mdv2010.0.noarch.rpm

<html><head><META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>4.&nbsp;Temporisation de haute pr&eacute;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&eacute;es / sorties sous Linux
    
    "><link rel="up" href="index.html" title="
    
        Petit guide de programmation des ports 
        d'entr&eacute;es / sorties sous Linux
    
    "><link rel="prev" href="ar01s03.html" title="3.&nbsp;Interruptions (IRQ) et acc&egrave;s DMA"><link rel="next" href="ar01s05.html" title="5.&nbsp;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.&nbsp;Temporisation de haute pr&eacute;cision</th></tr><tr><td align="left" width="20%"><a accesskey="p" href="ar01s03.html">Pr&eacute;c&eacute;dent</a>&nbsp;</td><th align="center" width="60%">&nbsp;</th><td align="right" width="20%">&nbsp;<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.&nbsp;Temporisation de haute pr&eacute;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.&nbsp;Temporisations</h3></div></div><div></div></div><p>
	Avant toutes choses, il est important de pr&eacute;ciser
	l'impossibilit&eacute; de garantir un contr&ocirc;le pr&eacute;cis des temps
	d'ex&eacute;cution de processus en mode utilisateur du fait de la
	nature multit&acirc;che du noyau Linux. Votre processus peut &ecirc;tre
	mis en sommeil &agrave; n'importe quel moment pour une dur&eacute;e allant de
	10&nbsp;millisecondes &agrave; quelques secondes (sur un syst&egrave;me
	dont la charge est tr&egrave;s importante). Malgr&eacute; tout, pour la 
	plupart des applications utilisant les ports d'entr&eacute;es / 
	sorties, cela n'est pas tr&egrave;s important. Si vous voulez minimiser 
	cet inconv&eacute;nient, vous pouvez donner &agrave; votre processus une 
	priorit&eacute; plus haute (reportez-vous &agrave; la page de manuel de 
	<span class="citerefentry"><span class="refentrytitle">nice</span>(2)</span>) ou faire appel &agrave; l'ordonnancement temps-r&eacute;el 
	(voir ci-apr&egrave;s).
      </p><p>
	Si vous souhaitez obtenir une pr&eacute;cision de temporisation plus
	&eacute;lev&eacute;e que celle qu'offre les processus en mode utilisateur 
	usuels, sachez qu'il existe des possibilit&eacute;s de support 
	&laquo;&nbsp;temps-r&eacute;el&nbsp;&raquo; en mode utilisateur. Les 
	noyaux Linux de la s&eacute;rie 2.x permettent de travailler en quasi 
	temps-r&eacute;el. Pour les d&eacute;tails, reportez-vous &agrave; la page de manuel 
	de
	<span class="citerefentry"><span class="refentrytitle">sched_setscheduler</span>(2)</span>.
	Il existe &eacute;galement des versions sp&eacute;ciales du noyau offrant un 
	vrai ordonnancement temps-r&eacute;el.
	
      </p><div class="sect3" lang="fr"><div class="titlepage"><div><div><h4 class="title"><a name="N10254"></a>4.1.1.&nbsp;Avec <code class="function">sleep()</code> et
	      <code class="function">usleep()</code>
	</h4></div></div><div></div></div><p>
	  Commen&ccedil;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&eacute;es au minimum 
	  de l'ordre de dizaines de millisecondes (10&nbsp;millisecondes
	  semblent &ecirc;tre la dur&eacute;e minimum), <code class="function">usleep()</code>
	  devrait s'av&eacute;rer suffisant. Ces fonctions lib&egrave;rent l'acc&egrave;s au
	  microprocesseur pour d'autres processus, &eacute;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&eacute;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&egrave;me), redonner le processeur aux 
	  autres processus prend &eacute;norm&eacute;ment de temps. En effet 
	  l'ordonnanceur des t&acirc;ches du noyau Linux (en tout cas pour les 
	  microprocesseurs de la famille x86) prend g&eacute;n&eacute;ralement au 
	  moins 10 &agrave; 30 millisecondes avant de rendre le contr&ocirc;le
	  au processus. De ce fait, dans les temporisations de courte 
	  dur&eacute;e,
	  <span class="citerefentry"><span class="refentrytitle">usleep</span>(3)</span> effectue en r&eacute;alit&eacute; une pause plus longue que 
	  celle sp&eacute;cifi&eacute;e en param&egrave;tre, prenant au moins 
	  10&nbsp;millisecondes suppl&eacute;mentaires.
	</p></div><div class="sect3" lang="fr"><div class="titlepage"><div><div><h4 class="title"><a name="N1027D"></a>4.1.2.&nbsp;<code class="function">nanosleep()</code></h4></div></div><div></div></div><p>
	  Dans les noyaux Linux de la s&eacute;rie 2.0.x est apparu l'appel
	  syst&egrave;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&egrave;s court (quelques microsecondes ou plus).
	</p><p>
	  Pour des attentes &le; 2 millisecondes, si (et seulement si) 
	  votre processus fonctionne en ordonnancement quasi temps-r&eacute;el 
	  (au moyen de 
	  <code class="function">sched_setscheduler()</code>), 
	  <code class="function">nanosleep()</code> fait appel &agrave; une boucle 
	  d'attente&nbsp;; 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&eacute;e par beaucoup de pilotes), 
	  la dur&eacute;e de celle-ci &eacute;tant calcul&eacute;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&ccedil;on 
	  pr&eacute;cise. Voyez <code class="filename">/usr/include/asm/delay.h</code> pour 
	  plus de d&eacute;tails quant &agrave; son fonctionnement.
	</p></div><div class="sect3" lang="fr"><div class="titlepage"><div><div><h4 class="title"><a name="N102AF"></a>4.1.3.&nbsp;Temporisations gr&acirc;ce aux ports d'entr&eacute;e / 
	       sortie</h4></div></div><div></div></div><p>
	  Les acc&egrave;s aux ports d'entr&eacute;e / sortie sont un autre moyen 
	  d'obtenir des temporisations. L'&eacute;criture ou la lecture d'un 
	  octet sur le port <code class="literal">0x80</code> (voir ci-dessus la 
	  proc&eacute;dure &agrave; suivre) devrait avoir pour cons&eacute;quence un 
	  retard d'une microseconde, ind&eacute;pendamment du type et de la 
	  cadence du microprocesseur. Vous pouvez donc proc&eacute;der de la 
	  sorte afin d'obtenir un retard de quelques microsecondes. 
	  L'&eacute;criture sur ce port ne devrait pas avoir d'effets 
	  secondaires sur une machine classique, pour preuve certains 
	  pilotes du noyau font appel &agrave; cette m&eacute;thode. C'est &eacute;galement 
	  de cette mani&egrave;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&eacute;cis&eacute;ment, une op&eacute;ration de lecture ou d'&eacute;criture sur 
	  la plupart des ports dans l'intervalle 
	  <code class="literal">0x000</code>-<code class="literal">0x3ff</code> prend 
	  1&nbsp;microseconde. Par exemple, si vous utilisez directement 
	  le port parall&egrave;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.&nbsp;Temporisations en assembleur</h4></div></div><div></div></div><p>
	  Si vous connaissez le type et la fr&eacute;quence du processeur de la 
	  machine sur laquelle votre programme va s'ex&eacute;cuter, vous 
	  pouvez coder en dur les temporisations en faisant appel &agrave; 
	  certaines instructions assembleur. Rappelez-vous cependant
	  qu'&agrave; tout moment votre processus peut-&ecirc;tre mis en attente par 
	  l'ordonnanceur et, de ce fait, les temporisations peuvent 
	  s'av&eacute;rer plus longues que souhait&eacute;es.
	</p><p>
	  Pour les donn&eacute;es du tableau ci-dessous, la fr&eacute;quence interne 
	  du microprocesseur d&eacute;termine le nombre de cycles d'horloge 
	  consomm&eacute;s. Par exemple, pour un microprocesseur &agrave; 50&nbsp;Mhz 
	  (un 486DX-50), un cycle d'horloge dure 1/50000000 de seconde 
	  (soit 200&nbsp;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 &ecirc;tre les m&ecirc;mes 
	  que ceux du 486, &agrave; 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-&ecirc;tre parfois &ecirc;tre combin&eacute;e avec une autre (cependant, du 
	  fait de l'algorithme d'ex&eacute;cution hors de s&eacute;quence 
	  (<span class="foreignphrase"><em class="foreignphrase">out-of-order</em></span>), il 
	  n'est pas n&eacute;cessaire qu'il s'agisse d'une instruction 
	  cons&eacute;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'&eacute;tat, mais cela reste sans gravit&eacute; 
	  puisque gcc devrait le d&eacute;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'&eacute;num&eacute;r&eacute;e dans le tableau ci-dessus. Si vous 
	  pr&eacute;f&eacute;rez grouper plusieurs instructions dans le m&ecirc;me appel &agrave; 
	  <code class="function">asm</code>, il vous suffit de les s&eacute;parer par 
	  des points-virgules. Par exemple 
	  <code class="function">asm("nop&nbsp;; nop&nbsp;; nop&nbsp;; 
	  nop")</code> ex&eacute;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&eacute;gr&eacute;es au code par gcc, &eacute;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.&nbsp;<code class="literal">rdtsc</code> pour Pentium</h4></div></div><div></div></div><p>
	  Avec les microprocesseurs Pentium, vous avez la possibilit&eacute; de 
	  conna&icirc;tre le nombre de cycles d'horloge &eacute;coul&eacute;s depuis le 
	  dernier red&eacute;marrage avec le code C suivant (qui fait appel &agrave; 
	  l'instruction appel&eacute;e RDTSC)&nbsp;:
	</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.&nbsp;Mesure du temps</h3></div></div><div></div></div><p>
	Pour des dur&eacute;es de la pr&eacute;cision d'une seconde, il est 
	certainement plus simple d'utiliser la fonction 
	<code class="function">time()</code>. Pour obtenir plus de pr&eacute;cision, 
	<code class="function">gettimeofday()</code> a une pr&eacute;cision d'environ 
	une microseconde (mais rappelez-vous de l'ordonnancement d&eacute;j&agrave; 
	&eacute;voqu&eacute; pr&eacute;c&eacute;demment). Pour les Pentium, le fragment de code 
	<code class="literal">rdtsc</code> ci-dessus est pr&eacute;cis au cycle 
	d'horloge pr&egrave;s.
      </p><p>
	Si vous souhaitez que votre processus re&ccedil;oive un signal apr&egrave;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&eacute;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&eacute;c&eacute;dent</a>&nbsp;</td><td align="center" width="20%">&nbsp;</td><td align="right" width="40%">&nbsp;<a accesskey="n" href="ar01s05.html">Suivant</a></td></tr><tr><td valign="top" align="left" width="40%">3.&nbsp;Interruptions (IRQ) et acc&egrave;s DMA&nbsp;</td><td align="center" width="20%"><a accesskey="h" href="index.html">Sommaire</a></td><td valign="top" align="right" width="40%">&nbsp;5.&nbsp;D'autres langages de programmation</td></tr></table></div></body></html>