<!doctype debiandoc system>

<book>

<title> Réglage fin de l'horloge sur un système linux

<author>Laurent PICOULEAU <email>lcrpic@a2points.com</email>

<version>v. : 0.9, le <date>

<abstract>
Ce document explique comment obtenir une bonne précision de l'horloge noyau 
à l'aide des outils <prgn>hwclock</prgn> et <prgn>adjtimex</prgn> et sans
avoir recours à <prgn>xntp</prgn> ou <prgn>ntpdate</prgn> sur un système linux.

<copyright>
Je place ce document dans le domaine public.<p>


<toc>

<chapt> Introduction <p>
Soyez un minimum attentif à ce que vous faites en matière d'horloge système : 
obtenir un réglage précis de l'horloge demande pas mal de temps. La fausser 
complètement ne demande que quelques secondes et une simple faute de frappe
peut y suffire. <p>
Dans ce document je mélange allègrement GMT et UTC même si d'un point de vue
strict ce ne sont pas les mêmes notions.

<sect> Motivations <p>
Ce texte ne se veut pas une référence mais est simplement un essai pour aider
les personnes ayant des problèmes avec leur horloge sous linux. Il se peut
que ce que je décris soit également applicable à d'autres systèmes Unix mais
je ne m'en suis absolument pas assuré.
</sect>

<sect> Prérequis et objectifs <p>
Cette méthode suppose que vous laissiez tourner en permanence votre machine
<footnote>du moins le temps d'étalonner l'horloge CMOS, soit environ une 
semaine</footnote>, que la machine ne tourne pas sous un autre système 
d'exploitation susceptible de modifier l'horloge CMOS manuellement ou 
automatiquement et enfin que votre horloge CMOS soit réglé sur l'heure GMT
<footnote><em>a priori</em>, si votre horloge CMOS n'est pas réglé sur l'heure 
GMT, il suffit de ne pas mettre les options -u (ou --utc) mais je ne sais pas 
comment est géré le changement d'heure lors du passage de l'heure d'hiver à 
l'heure d'été et inversement</footnote>. Enfin, il vous faut avoir accès à
une horloge de référence fiable<footnote>l'horloge parlante me semble plus
indiquée que le carillon du clocher voisin mais c'est plus cher et moins
buccolique</footnote> et un noyau intégrant la gestion précise du temps, je
ne sais pas à partir de quelle version exacte cela est bien le cas mais les
noyaux 2.0 l'ont déja.
</sect>
</chapt>

<chapt> Pour les impatients <p>
Voici une description sans explication de la méthode à mettre en oeuvre pour
atteindre une précision de l'horloge système de l'ordre du dixième de seconde
de dérive par jour.

<sect> Remarque ironique <p>
Vous allez effectivement lire rapidement ce chapitre mais sa mise en oeuvre
vous demandera une semaine : obtenir un décompte précis du temps en demande
pas mal justement...
</sect>

<sect id="bref"> Plan d'action <p>
Toutes les manipulations indiquées sont à faire en root
<list>
  <item>1° Jour <p>
    <list compact>
      <item>Via le BIOS<footnote>ou toute autre méthode</footnote> mettre
        l'horloge CMOS à l'heure UTC «en gros»
      <item>Installer <prgn>hwclock</prgn> et <prgn>adjtimex</prgn>
      <item>Lancer : <tt>hwclock --set --date="MM/JJ/AA hh:mm:00" --utc 
	<footnote>attention au format de la date : mois/jour/année</footnote>
        --debug</tt> au moment où le <em>quatrième top</em> vous l'indique
	<footnote>c'est l'heure locale qu'il faut mettre <em>surtout pas</em> 
	l'heure GMT</footnote>
    </list>
  <item>2° Jour <p>
    <list compact>
      <item>Lancer : <tt>hwclock --set --date="MM/JJ/AA hh:mm:00" --utc
        --debug</tt>
      <item><em>Si</em> la première valeur de la première ligne du 
        fichier <tt>/etc/adjtime</tt> est supérieure à 8 en valeur
	absolue <em>alors</em>
	<list compact>
          <item>lancer : <tt>adjtimex -u -c</tt>
	  <item>relever la valeur suggérée pour «tick»
	  <item>lancer : <tt>adjtimex -u -t valeur_suggérée</tt>
	</list>     
      <item>Lancer : <tt>adjtimex -u -l=/var/log/clocks.log</tt>
    </list>
  <item>7° Jour <p>
    <list compact>
      <item>Lancer : <tt>adjtimex -u -l=/var/log/clocks.log</tt>
      <item>Lancer : <tt>hwclock --set --date "MM/JJ/AA hh:mm:00" --utc 
        --debug</tt>
      <item>Lancer : <tt>adjtimex -u -l=/var/log/clocks.log</tt>
<!--  <item>Lancer : <tt>adjtimex -u -w -l=/var/log/clocks.log</tt> et appeler
        l'horloge parlante<footnote>ou autre référence</footnote>
******************************************************************************	
     c'est idiot de se synchroniser avec une horloge externe alors qu'on vient
     de synchroniser l'horloge CMOS. Déterminer comment utiliser hwclock 
     -hctosys utilement
******************************************************************************     
  -->
      <item>Lancer : <tt>hwclock --utc --hctosys</tt>
<!--
******************************************************************************
Ca ne donne pas autant d'info que adjtimex -w (manque la précision) mais c'est
plus exact. Je retiens donc cela en attendant une meilleure idée
******************************************************************************
  -->
      <item>Lancer : <tt>adjtimex -u -l=/var/log/clocks.log</tt>
      <item>Lancer : <tt>adjtimex -u -c=22 -i=100</tt><footnote>oui, je sais,
        ça prend plus d'une demi heure alors que le chapitre s'appelle pour les
	impatients</footnote>
      <item>Faire la moyenne des vingt valeurs suggérées pour «freq»
      <item>Lancer : <tt>adjtimex -u -f moyenne_calculée</tt>
      <item>Lancer : <tt>adjtimex -u -l=/var/log/clocks.log</tt>
    </list>  
</list>
<em>Enfin !</em> Votre horloge système est maintenant calée sur votre référence
et ne s'en décalera que d'environ un dixième de seconde par jour.
</sect>

</chapt>

<chapt> Méthodologie détaillée <p>
En fait, il suffit de procéder à plusieurs étapes pour pouvoir transformer
le plus humble des 386SX en horloge présentant une dérive de l'ordre du
dixième de seconde par jour. <p>
<list>
  <item>
    On synchronise l'horloge CMOS et l'horloge de référence
  <item>
    On laisse l'horloge CMOS dériver pendant quelques jours
  <item>
    On synchronise de nouveau l'horloge CMOS et l'horloge de référence et on en
    profite pour déterminer de combien de secondes par jour l'horloge CMOS se
    décale (la dérive quotidienne). A partir de là on peut utiliser cette 
    horloge CMOS comme référence bien qu'elle ne soit pas précise. En effet, 
    connaissant le dernier instant où elle à été synchrone avec l'horloge de
    référence et sa dérive quotidienne, il est simple de connaitre l'heure
    indiquée par l'horloge de référence avec une bonne précision.
  <item>
    Juste après, on synchronise l'horloge système avec l'horloge CMOS.
  <item>
    On détermine les valeurs correctes des variables du noyau gérant l'horloge
    système et on ajuste ces variables aux valeurs trouvées. La détermination
    des variables se fait en s'appuyant sur l'horloge CMOS maintenant 
    étalonnée.
</list><p>

Pour utiliser hwclock, il est préférable de passer par /dev/rtc et d'avoir
intégré la gestion de l'horloge RTC<footnote>qui n'est autre que l'horloge CMOS
</footnote> <p>

Je vous conseille d'enregistrer les modifications que vous faites à tout ce qui
à trait à l'horloge dans un fichier pour pouvoir essayer de réparer les dégats
en cas de fausse manipulation. Par ailleurs, enregistrer via <tt>adjtimex -u
-l=/var/log/clocks.log</tt> les ajustements que vous faites vous aidera à 
corriger une dérive de long terme qu'il serait malcommode de circonvenir par les
moyens détaillés ici.<footnote>voir <tt>man 8 adjtimex</tt> en particulier 
l'option -r (ou --review pour les amateurs d'options longues)</footnote>

<sect> Mise au point sur les commandes <p>
<tt>adjtimex</tt> n'a d'influence que sur l'horloge système.<footnote>c'est à 
dire l'horloge maintenu par le noyau, celle qui est utilisé pour dater les
fichiers, afficher l'heure...</footnote><p>
<tt>hwclock</tt> est la seule façon d'affecter l'horloge CMOS.<p>
Les deux commandes utilisent le fichier <tt>/etc/adjtime</tt>.<p>
Les options <tt>--systohc</tt> et <tt>--hctosys</tt> de <tt>hwclock</tt> 
permettent, respectivement, de copier l'horloge système dans l'horloge CMOS et
de faire la manipulation inverse : CMOS vers système. Elles permettent par 
ailleurs de fixer la même heure pour les 2 horloges à quelques micro-secondes
près.<p>
Evitez d'utiliser <tt>date --set</tt> pour régler l'heure système.<p>
</sect>

<sect> Etalonner l'horloge CMOS avec hwclock<p>
Il peut sembler paradoxal de passer un temps important à régler l'horloge
CMOS alors que l'horloge système est plus précise mais, cela est nécessaire
pour deux raisons. Tout d'abord cela permet d'affiner la précision de l'horloge
système en ayant un moyen commode de déterminer les bonnes valeurs de «tick» et
«freq» et d'autre part, le circuit qui gère l'horloge CMOS est aussi 
responsable du signal 100 Hz qui sert de base à l'horloge système. Donc la 
connaissance acquise sur l'horloge CMOS est directement utile pour régler la
fréquence de l'horloge système. Enfin, cet étalonnage précis de l'horloge CMOS
va permettre de conserver l'heure avec précision lors des arrêts ou «reboot»
de la machine.<p>

Pour le détail syntaxique des commandes je vous renvoie à <ref id="bref">. Je 
vais détailler ici le "qu'est ce qu'on fait" et le "pourquoi on le fait" plutôt 
que le "comment" puisque les pages man répondent déja fort complètement à cette
dernière question.<p>

Passons maintenant au détail et à la justification des manipulations proposées.
<p>

<sect1> Premier jour<p>
L'installation de <prgn>hwclock</prgn> est très probablement déja faite sur 
votre système car il fait partie de <prgn>util-linux</prgn>. Par contre 
<prgn>adjtimex</prgn> a moins de chance d'être installé par défaut. L'option
<tt>--debug</tt> de <prgn>hwclock</prgn> n'est pas nécessaire, c'est juste que 
j'ai trouvé intéressant de voir comment le système s'y prend.<p>

Si vous ne savez pas accéder au réglage de l'heure via le BIOS, c'est sans
importance, cette étape n'est pas indispensable, elle n'est là que pour
éviter des gags possibles si jamais la dérive indiquée dans <tt>/etc/adjtime
</tt> était par trop délirante. Normalement en respectant l'ordre proposé pour
les manipulations même si vous débutiez avec une dérive de 3 jours par jour (!)
vous obtiendrez quand même une bonne précision en une semaine.<p>

La commande <prgn>hwclock</prgn> a un double but : le premier est bien
évidemment de synchroniser l'horloge CMOS sur l'horloge de référence, le 
deuxième est de créer le fichier <tt>/etc/adjtime</tt> s'il n'existait pas
déja. La valeur de dérive<footnote>la première de la première ligne</footnote>
est très probablement irréaliste. Ce qui nous intéresse ici, c'est la deuxième
ligne qui enregistre le moment (en seconde depuis le 1/1/1970 à 0h00) ou l'heure
a été réglée pour la dernière fois.<p>

Si vous n'avez pas su régler l'horloge CMOS via le BIOS, votre heure système 
est alors fausse, réglez la donc avec <tt>hwclock --hctosys --utc</tt>
</sect1>

<sect1> Deuxième jour<p>
Du strict point de vue de l'obtention d'une horloge précise, les manipulations
indiquées pour le deuxième jour ne servent à rien ! Elles ne sont là que pour
éviter de laisser les deux horloges diverger à cause d'une valeur inexploitable
de la dérive. On peut en fait les faire au bout de quelques heures, le but étant
simplement de déterminer l'ordre de grandeur de la dérive quotidienne et de 
prendre une mesure corrective si cette dérive est vraiment importante.<footnote>
une dérive de trente secondes par jour est fort possible, ne pas s'étonner. Par
contre si vous obtenez une dérive énorme (plusieurs milliers de seconde), c'est
que vous vous êtes mélangé les pinceaux avec l'heure GMT</footnote><p>
La commande <tt>adjtimex</tt> finale ne sert qu'à enregistrer les valeurs 
courantes des divers paramètres liées aux horloges<footnote>je ne suis pas sur
qu'il soit pertinent d'enregistrer ces paramêtres alors que ni l'horloge CMOS,
ni l'horloge système ne sont étalonnées, mais je préfère enregistrer toutes les
infos possibles touchant à l'horloge système</footnote>
</sect1>

<sect1> Septième jour<p>
C'est là que tout se passe !<p>
On règle de nouveau l'horloge CMOS grace à l'horloge de référence. Cela à pour
conséquence de mettre à jour l'horloge CMOS bien sur mais aussi et, c'est ce qui
nous intéresse, la dérive est déterminée avec une bonne précision.<footnote>qui
dépend de vos réflexes voir <ref id="préc"></footnote><p>
On profite de ce que l'horloge CMOS vient d'être mise à l'heure pour régler
l'horloge système. La partie la plus longue du réglage est faite<footnote>sauf
si une précision de l'ordre du dixième de seconde de dérive par jour ne vous 
suffit pas</footnote> ; l'horloge CMOS est étalonnée et elle est à l'heure 
puisqu'on vient de la régler. Elle va donc pouvoir nous servir d'horloge de 
référence pour la suite des opération. En effet, même si elle a une dérive 
importante, on peut grace à elle déterminer l'heure de référence : la deuxième
ligne de <tt>/etc/adjtime</tt> nous indique quand elle a été réglée et la dérive
quotidienne est indiquée dans ce même fichier. Ces deux éléments et le temps
écoulé depuis qu'elle a été réglée (ce dernier étant obtenu grace à l'horloge
système) permette de calculer l'heure de référence.
</sect1>
</sect>

<sect> Régler l'horloge système avec adjtimex <p>
L'horloge système peut se régler très finement (de l'ordre du millionème de
seconde de dérive par jour) grace au paramêtres «tick» et «freq». Notre 
objectif est maintenant de déterminer ces valeurs afin que l'horloge système
évolue à la même vitesse que l'horloge CMOS corrigée de sa dérive quotidienne.
Il y a, au moins, deux stratégies possible pour arriver à ce résultat. La 
première est de copier la méthode employée pour régler l'horloge CMOS : attendre
suffisament longtemps pour connaître la dérive quotidienne. La deuxième est
de se servir de l'horloge CMOS comme étalon.<p>
Voici pourquoi j'ai préféré la seconde méthode :
<list>
  <item>
    C'est beaucoup plus rapide
  <item>
    C'est plus précis : on mesure la différence entre les horloges CMOS et
    système a moins de 25 micro-secondes près alors qu'on mesure la différence
    entre l'horloge système et l'horloge de référence à environ une demi-seconde
    près.
  <item>
    Le système peut calculer «tick» et «freq» pour vous.
</list> <p>

L'inconvénient est que si vous vous êtes planté pour l'horloge CMOS, vous allez
provoquer la même erreur pour l'horloge système.<p>

La commande <tt>adjtimex -u -c=22 -i=100</tt> vous permet d'obtenir 20 valeurs
de ces paramêtres. Les nombres 22 et 100 dans cette commande n'ont rien de 
scientifiquement déterminés, ils ont été choisis sur les critères suivants :
<list>
  <item>
    connaitre «freq» à 76 000 près<footnote>cela correspond à une erreur de un 
    dixième de seconde par jour</footnote>. Sur mon système laisser la valeur
    par defaut de <tt>-i</tt> donnait des variations très supérieures à cette
    tolérance, j'ai même du utiliser 500 pour y arriver, mais sur un autre 
    système 50  est plus que suffisant obtenir la précision voulue. J'espère 
    que la valeur 100 suffira dans beaucoup de cas.
  <item>
    avoir suffisament de valeurs pour pouvoir faire quelques statistiques (écart
    type notamment)
  <item>
    garder un temps d'exécution pas trop long : il faut 22 * 100 secondes pour
    que cette commande se termine
</list> <p>

Je vous déconseille d'utiliser <tt>adjtimex -a</tt> à la place, en effet celui
ci ne retient qu'une valeur (la dernière) et non la moyenne des valeurs 
obtenues.


</sect>
</chapt>

<chapt> Conserver durablement la précision obtenue<p>
Après tous ces efforts pour régler votre horloge système, il serait dommage que
le premier arrêt venu vous fasse perdre tout vos réglages. Il suffit de prendre
quelques précautions pour pouvoir conserver une horloge précise même en cas
d'arrêt de la machine.

<sect> Préserver les réglages en cas de shutdown <p>
En fait, il suffit de connaître un petit nombre d'éléments pour d'une part
conserver l'étalonnage effectué et d'autre part redéterminer l'heure juste lors
du redémmarage.

<sect1> L'arrêt <p>
Commençons par les plus faciles : «tick» et «freq». Ils sont constants,
<footnote>du moins je ne pense pas qu'ils doivent évoluer, une fois la 
précision voulue atteinte, à moins d'un changement matériel</footnote> il 
suffit de créer un fichier qui s'écécute lors du démarage de la machine et 
qui contienne la commande <tt>adjtimex -u -f freq -t tick</tt>. Pour ceux qui 
préfèreraient que les valeurs en cours soient sauvegardées lors du shutdown 
il vous faudra concevoir un script qui récupère ces valeurs via la commande 
<tt>adjtimex -u -p</tt> et les place dans le fichier susmentionné. <p>

Une donnée qu'il est important de préserver est la dérive de l'horloge CMOS et
ce d'autant plus que la connaître avec précision demande beaucoup de temps.
Comme on est jamais à l'abri d'une erreur de manipulation, je garde une copie
du fichier <tt>/etc/adjtime</tt>. En cas de problème, il suffit de remplacer
la première valeur de la première ligne par celle figurant dans votre
sauvegarde. <p>

Enfin, il faut préserver l'heure qu'il était au moment où le «shutdown» a été
fait. Comme par ailleurs l'horloge système est plus précise que l'horloge CMOS,
il est préférable que ce soit l'heure système qui soit enregistrée. La commande
<tt>hwclock --utc --systohc</tt> remplit parfaitement ce rôle. En effet, elle 
enregistre dans le fichier <tt>/etc/adjtime</tt> l'instant auquel l'horloge
CMOS a été réglée qui n'est alors autre que l'heure système où cette commande a
été executée. Par ailleurs, elle enregistre aussi, en première ligne, l'instant
où l'horloge CMOS a été ajustée<footnote>voir <tt>man 8 hwclock</tt></footnote>. 
En ce cas
cette valeur coïncide avec la précédente. Il suffit donc de faire exécuter
cette commande par un script lancé lors du shutdown <em>avant</em> que le
répertoire racine ne soit démonté. Par contre elle présente l'inconvénient de
recalculer la dérive. Normalement la valeur recalculée devrait être proche de
la valeur précédente. <p>

Ceci fait, on va pouvoir lors du prochain redémmarage calculer l'heure qu'il
est et conserver la précision qui avait atteinte en ce qui concerne la dérive
quotidienne.
</sect1>

<sect1> Le redémmarage <p>
Les choses sont assez simples et sont probablement déja faites par votre
distribution, je les détaille au cas où ce ne serait pas le cas.
Il y a deux choses à faire : calculer l'heure qu'il est et régler l'horloge 
système sur cette heure. <p>

Le calcul de l'heure incombe à la commande <tt>hwclock --utc --adjust</tt> qui
relève l'instant où l'horloge CMOS a été ajustée pour la dernière
fois,<footnote>c'est la seconde valeur de la première ligne</footnote> 
soustrait celui ci de la valeur courante de l'horloge CMOS pour déterminer
combien de jour se sont écoulés depuis ce dernier ajustement, multiplie ce
nombre de jours par la dérive quotidienne et soustrait le résultat à la valeur
courante de l'horloge CMOS. <p>

Pour régler ensuite l'horloge système, la commande a déja été donnée, il s'agit
de <tt>hwclock --utc --hctosys</tt>.
</sect1>
</sect>

<sect> Survivre à l'arrêt intempestif<p>
Il y a de grande chances que vous ayez à résoudre des pbs plus stressants que la
simple conservation de la précision de votre horloge système. Je ne détaille 
pourtant ici que ce seul problème : désolé...<p>
En fait si vous avez bien étalonné votre horloge CMOS et que vous avez 
enregistrer quelque part tick et freq vous êtes sauvé. En effet, du point de
vue de l'horloge les seules différences avec un shutdown correct sont que
l'heure système n'a pas été copiée dans l'heure CMOS et le fichier /etc/adjtime
n'a pas été mis à jour. Lors du redémarrage l'étalonnage de l'horloge CMOS va
s'avérer bien utile... Lors du <tt>hwclock --utc --adjust</tt> la commande
s'écute exactement comme décrit précedemment. Toutefois, l'instant de dernier
est plus vieux puisque l'horloge CMOS n'a pas pu être réglée lors du shutdown.
</sect>
</chapt>

<chapt> Remarques diverses <p>
<sect id="préc"> De la précision indiquée
</sect>

<sect> De la détermination de freq et de mes hésitations statistiques
</sect>

<sect> De la variabilité des fréquences de quartz et autres sources d'erreur
</sect>
</chapt>

</book>

