cvs commit: src/usr.bin/vmstat vmstat.c src/usr.bin/w w.c

Poul-Henning Kamp phk at phk.freebsd.dk
Thu Oct 20 05:44:54 PDT 2005


In message <20051020211131.A874 at delplex.bde.org>, Bruce Evans writes:

>POSIX's specification of CLOCK_MONOTONIC seems to be missing leap seconds
>problems.  It seems to be required to actually work; thus it should give
>the difference in real time, in seconds with nanoseconds resolution,
>relative to its starting point, so it must include leap seconds.

It doesn't make sense to talk about a leap seconds on a timescale
which is not UTC because leap-seconds by definition only exist in UTC.

CLOCK_MONOTONIC is defined as a count of seconds (lets tacitly
assume they mean SI seconds here) from an arbitrary origin.

A better and unambiguous way to write that would have been:

	CLOCK_MONOTONIC = TAI + alpha

It follows from this that CLOCK_MONOTONIC does not know what a
leap-second is and doesn't notice them happening.

Because of our particular choice of alpha, CLOCK_MONOTONIC is also
a very convenient measure of how many seconds the kernel has been
running.

>Of course it can't reasonably be expected to have nanoseconds accuracy.
>[...]

It certainly can and should be expected to and it does.

>difftime() also seems to be required to actually work.  According to
>draft C99 (n869.txt):
>
>%        [#2] The difftime function computes the  difference  between
>%        two calendar times: time1 - time0.

Again, this is another example of computer-geeks missing the finer
points in timekeeping.

The word "calendar" refers to things counting time in units of days.

The above text therefore conveys no usable information about how
leap-seconds should be accounted for, since leap seconds by definition
are intra-day.

> [clarification from other standard that the two time_t comes from time(2)]

>time_t's cannot be naively subtracted in general in C, so the difference
>here must be formal.  The difference is required to contain leap seconds
>by POLA.

Yeah, right: in your dreams...

You can by definition not implment difftime correctly since the
time_t timescale does not contain any indication of leapseconds.

This means that there is no way to tell which side of an inserted
leapsecond a time(2) timestamp comes from:

	UTC		time(2)
	23:59:57	N-3
	23:59:58	N-2
	23:59:59	N-1
	23:59:60	N
	00:00:00	N
	00:00:01	N+1

Worst case, difftime() will be wrong by two seconds: taking the difference
from one leapsecond to another and guessing wrong in both ends.

The fact that mktime() and timegm() gets it wrong the other way because
of DWIM logic is merely ising on the cake.

>Back to the utilities: according to the standards, it seems to be equally
>correct to implement "double uptime()" as:
>
> 	/* Done in kernel; happens to give 0 in FreeBSD implementation: */
> 	clock_gettime(CLOCK_MONOTONIC, &boottime);
>
> 	clock_gettime(CLOCK_MONOTONIC, &now);
>
> 	return (now.tv_sec - boottime.tv_sec +
> 	    1e-9 * (now.tv_nsec - boottime.tv_nsec);

On FreeBSD this delivers the correct answer.

>and as:
>
> 	/* Done in kernel; nonzero except if you booted in 1970: */
> 	clock_gettime(CLOCK_REALTIME, &boottime);
>
> 	clock_gettime(CLOCK_REALTIME, &now);
>
> 	/* Restore leap seconds if necessary; lose nanoseconds resolution: */
> 	return difftime(now.tv_sec, boottime.tv_sec);

This suffers from the +/-2 second error from difftime(2) and will
return the wrong result if the clock is stepped.

-- 
Poul-Henning Kamp       | UNIX since Zilog Zeus 3.20
phk at FreeBSD.ORG         | TCP/IP since RFC 956
FreeBSD committer       | BSD since 4.3-tahoe    
Never attribute to malice what can adequately be explained by incompetence.


More information about the cvs-src mailing list