[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Hora y BIOS



Juan Leseduarte wrote:
> Supongo que consiste en el mensaje que ya enviaron a esta misma lista hace un
> tiempo. Era realmente muy interesante, y era específico para Debian, ya que

Como supongo que hablas del email que escribí en noviembre: ¡muchas
gracias! El documento En-Hora-Como.html no tiene nada que ver con mi
email de noviembre.

> está basado en los scripts que tiene por defecto Debian.  No obstante, creo
> que hay un fallo (ojo, en los scripts de Debian, no en el documento). Me
> explico: La idea del método es: Al arrancar Debian, el reloj del sistema
> pregunta al reloj de hardware la hora que es, éste responde, pero el el
> sistema no se la cree, si no que dice: bueno como que desde la última vez que
> se apagó Debian ha pasado tanto tiempo, y como sé ( porque lo dice así
> /etc/adjtime ) que el reloj de hard necesita un ajuste de X segundos por  día,
> la hora que doy por buena es:
> 
> hora que da el reloj de hard + X*número de días (en decimal) que han pasado.

Correcto: todo ello lo hacen las siguientes dos líneas del fichero
/etc/rcS.d/S50hwclock.sh -> ../init.d/hwclock.sh:
hwclock --adjust $GMT
hwclock --hctosys $GMT

La primera "arregla" la hora del reloj hardware basándose en cuánto
tiempo lleva el reloj hardware sin ser corregido y su atraso X. Estos
dos datos se obtienen de /etc/adjtime. Este fichero contiene como datos:
1) atraso (en segundos/día, si el reloj hardware adelanta entonces el
atraso es negativo), 2) fecha de la última corrección (en segundos desde
nochevieja de 1969), 3) un cero, 4) fecha de la última calibración (en
segundos desde nochevieja de 1969).

Aquí es importante distinguir entre calibración y corrección. La
corrección es cuando se pone el reloj hardware a una hora, mientras que
en una calibración esta hora se considera fiable y se puede usar para
cálculos posteriores. El reloj hardware se calibra con las opciones
--systohc y --set. La opción --adjust no lo calibra, sino que sólo lo
corrige. Por tanto, en terminología de conjuntos puede decirse que
corrección = ajuste U calibración (U=unión).

Sabiendo qué hora marca el reloj hardware ahora, h1 (en s), qué hora
marcaba en su última corrección, h0 (en s), y el atraso X (en s/día),
puede calcularse qué hora h es ahora (en s). La fórmula que hwclock usa
para la opción --adjust es inch=FLOAT((h1-h0)*X/86400+0.0), con FLOAT
una macro para convertir números decimales en enteros de forma que si
x=FLOAT(y) entonces x<=y<x+1, x entero. Si inch es mayor que 0 o menor
que -1 entonces aplica h=h1+inch y actualiza /etc/adjtime de manera que
su segundo campo (fecha de última corrección) sea h.

Una vez que el reloj hardware está corregido, la segunda línea pone la
hora del sistema a lo que marque el reloj hardware.

Cuando el sistema se resetea, uno de los cuatro últimos scripts que se
ejecutan es /etc/rc6.d/S25hwclock.sh con la opción stop. Entonces se
guarda la hora que tenga el sistema en el reloj hardware hasta el
momento en que se vuelva a encender el sistema (al cabo de unos
segundos, horas o días). Esto lo hace con la línea:
hwclock --systohc $GMT

La opción --systohc ejecuta una calibración del reloj hardware (usando
como fuente la hora del sistema). Además de cambiar la hora del reloj
hardware, modifica los campos 1, 2 y 4 del fichero /etc/adjtime. Los
campos 2 y 4 los pone a la hora actual (en segundos tras la medianoche
de 1969). El campo 1 lo calcula de una forma totalmente INESPERADA y que
no se ajusta a lo que comenta el manual de hwclock. En concreto el
manual dice:

       Every  time  you calibrate (set) the clock (using --set or
       --systohc ), hwclock  recalculates  the  systematic  drift
       rate based on how long it has been since the last calibra­
       tion, how long it has been since the last adjustment, what
       drift rate was assumed in any intervening adjustments, and
       the amount by which the clock is presently off.

Sin embargo, el código fuente de hwclock para las opciones --systohc y
--set llaman a una función (adjust_drift_factor) con cuatro argumentos:
1) puntero a estructura con los datos del fichero /etc/adjtime, 2) hora
a la que hay que poner el reloj hardware, 3) y 4) hora que marca el
reloj hardware. Los comentarios a esta función dicen que:

  We assume that the user has been doing regular drift adjustments
  using the drift factor in the adjtime file, so if <nowtime> and
  <clocktime> are different, that means the adjustment factor isn't
  quite right.

lo cual es un poco sospechoso, porque no dice que explícitamente
compruebe que ha pasado un tiempo despreciable desde la última
corrección. Dice que supone que antes de llamar al hwclock (--set o
--systohc) el usuario (root) ha ido llamando a hwclock regularmente con
--adjust. La prueba de todo ello es que lo que se calcula como
factor_adjust es 86400 * (hora real - hora del hardware) / (hora del
hardware - hora de la última calibración). Si el usuario no ha ejecutado
(como root) "hwclock --adjust" nunca desde el arranque del sistema,
entonces el factor_adjust calculado es el nuevo drift_factor. Si el
usuario ha ejecutado "hwclock --adjust" (y realmente el reloj hardware
se ha corregido, pues no se corrige si la corrección inch es 0 ó -1
segundos) inmediatamente antes de hacer "hwclock --set" o "hwclock
--systohc" entonces el factor_adjust calculado es realmente una
corrección al drift_factor que se ha aplicado con --adjust.

Y eso es lo que la función supone, pues unas líneas más abajo tiene:

    adjtime_p->drift_factor += factor_adjust;

> Lo que a yo observo es que el valor de X se actualiza mal. Este valor (el
> primer número que aparece en /etc/adjtime) debería ser más o menos el mismo.
> Sin embargo observo que los ajustes se van acumulando, y si tenía al principio
> (el 10 de marzo pasado) como "suggested adjustment" = -0.3951 sec/day (por
> cierto nada más hacer hwclock --systohc ya cambió algo) ahora voy por:
> -12.114908

Por lo tanto, Juan Leseduarte ha comprobado experimentalmente que la
función adjust_drift_factor no funciona como todos esperábamos (como
habíamos leído en el man de hwclock), y efectivamente actualiza mal el
valor de X.

> ¿Alguien me puede corregir si estoy equivocado? ¿Nadie ha observado esta
> irregularidad? ¿Sería un error de los scripts o de hwclock?

Parece ser que no estás equivocado. Me gustaría haber observado esta
irregularidad, pero uso el paquete xntp3 para mantener la hora del
sistema sincronizada con servidores NTP (demonio xntpd lanzado tras
haber puesto el reloj del sistema con ntpdate) y por tanto paso
olímpicamente de lo que dice el reloj hardware.

Con respecto a la última pregunta, yo diría que la culpa la tiene una
falta de entendimiento entre el programador de hwclock y el escritor del
manual de hwclock. Supongo que el que programó el script hwclock.sh de
init (miquels@cistron.nl) se leyó (como yo) sólo el manual de hwclock, y
no se puso a comprobar el código fuente. Por ello, la forma rápida de
solucionarlo sería incluir una línea "hwclock --adjust $GMT" antes de
"hwclock --systohc $GMT" del fichero /etc/init.d/hwclock.sh.

Sin embargo, la mejor forma de solucionarlo sería modificar la función
adjust_drift_factor. Si llamamos now a la hora verdadera ahora, hw a
lo que marca el reloj hardware ahora, hw1 a lo que marcaba el reloj
hardware DESPUÉS de hacer el último hwclock --adjust, y hw0 a lo que
marcaba el reloj hardware DESPUÉS de hacer el último hwclock --systohc
(o --set válido), y X0 al drift_factor viejo, he demostrado (la
demostración ocupa un folio, se la puedo mandar por fax a quien la
quiera) que la cantidad que hay que sumar a X0 (en segundos/día) para
obtener un drift_factor actualizado es:

incX=(now-hwa)*(86400+X0)/(hwa-hw0)

siendo hwa=hw+(hw-hw1)*X0/86400 lo que marcaría el reloj hardware si se
hiciera un --adjust justo antes del --systohc ó --set.

En conclusión, habría que cambiar las siguientes líneas de la función
adjust_drift_factor en hwclock.c:

    const float factor_adjust = 
      ((float) (nowtime - hclocktime) 
       / (hclocktime - adjtime_p->last_calib_time))
        * 24 * 60 * 60;
por:

   const float hclocktimeadjusted = hclocktime + ((float) hclocktime -
adjtime_p->last_adj_time) * adjtime_p->drift_factor / 86400;
   const float factor_adjust = (nowtime - hclocktimeadjusted) * (86400 +
adjtime_p->drift_factor) / (hclocktimeadjusted -
adjtime_p->last_calib_time);

Supongo que todo esto le interesaría al mantenedor del paquete
util-linux (donde está el programa hwclock y el script hwclock.sh) y
habría que mandar un aviso de bug a debian. Sin embargo, no es algo que
me interese especialmente (pues yo paso del hwclock). Si queréis
vosotros mandar un bug report a Debian con la información que os doy,
adelante, pero me temo que no servirá de nada: he visto la "Debian Bug
report logs" del paquete util-linux y ¡hay bugs en fase "outstanding"
desde hace más de dos años!

> Actualmente voy 1 minuto y 32 segundos atrasado. Comparado con el
desmadre que
> tenía antes está bastante bien, pero creo que no es del todo satisfactorio.
> Sobre todo teniendo en cuenta que para hacer el ajuste con el servidor de hora
> de SLUG tuve el ordenador encendido todo un fin de semana para hacer 2
> adjtimex bien separados y tener un calibrado fiable.

Yo pasaría del adjtimex (purgaría el paquete), y pondría la hora con un
"ntpdate -b -s hora.rediris.es" (hora.rediris.es es una servidor NTP de
tipo "stratum 1") en el script /etc/init.d/xntp3 que se crea al instalar
el paquete xntp3. Supongo que el demonio xntpd aprovechará los pocos
momentos en que tengas conexión internet para ajustar la hora del
sistema. Te voy a dar una receta para poner a punto tu sistema (has de
ser root) sin que tengas que tener el ordenador encendido.

# adjtimex -tick 10000 -frequency 0 (esto resetea las variables del
kernel)
# dpkg --purge adjtimex
# dselect
Selecciona e instala el paquete xntp3 con la opción de que ejecute
ntpdate al arrancar el sistema. Como servidor NTP puedes decirle que use
hora.rediris.es, que está en Madrid, o cualquier otro que conozcas
# echo "0.0 0 0.0" > /etc/adjtime
# . /etc/default/rcS
# [ "$GMT" = "-u" ] && GMT="--utc" (para tener la variable GMT a su
valor correcto)
# /etc/init.d/xntp3 stop ; /etc/init.d/xntp3 start
# hwclock --systohc $GMT
# joe /etc/init.d/hwclock.sh (joe o cualquier otro editor de texto)
La línea 32 debería quedar asÍ:
"hwclock --adjust $GMT; hwclock --systohc $GMT # modificada el
24-abr-2000"
# mv /etc/init.d/hwclock.sh /etc/init.d/hwclock.sh.dpkg-dist

Ahora sólo has de acordarte de no hacer nunca ningún hwclock (el sistema
ya lo hará por ti al arrancar y al apagar el ordenador). Si no tienes
una conexión continua a internet no sé si xntpd aprovechará los momentos
en que hay conexión para comprobar el reloj. Si al cabo de unos días
crees que no funciona como es debido siempre puedes comprobar la hora
con:
$ /usr/sbin/ntpdate -q hora.rediris.es ; date (no hace falta que seas
root)
Si la hora del sistema se ha ido demasiado puedes ponerla bien con:
# /etc/init.d/xntp3 stop ; /etc/init.d/xntp3 start (has de ser root)

Espero que con el cambio al hwclock.sh el primer número de /etc/adjtime
se mantenga más o menos constante, y que con xntp3 la hora se te
mantenga correctamente.

-- 
Conrado Badenas <Conrado.Badenas@uv.es>
PhD student                  | Assistant Lecturer
Department of Thermodynamics | Department of Optics
---------------------------------------------------
Faculty of Physics. University of Valencia
c/. Dr. Moliner, 50
46100 Burjassot (Valencia) - SPAIN


Reply to: