Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

gettimeofday() vs time()

1,158 views
Skip to first unread message

phil-new...@ipal.net

unread,
Nov 3, 2001, 11:52:14 PM11/3/01
to
Under what conditions should the following produce output:

#include <stdio.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
int main() {
struct timeval tod;
time_t tim;
for (;;) {
gettimeofday( & tod, NULL );
tim = time( NULL );
if ( tim < tod.tv_sec ) {
printf( "gettimeofday() = [%lu, %lu], time() = %lu\n",
(unsigned long) tod.tv_sec,
(unsigned long) tod.tv_usec,
(unsigned long) tim );
}
}
}

I would think that the possible range of values for time() would
be anywhere from the current time truncated to the nearest second
to the current time rounded to the nearest second. Since time()
is called after gettimeofday() I would think it could not be less.
However, this seems to happen a lot. I've seen this happen in both
Solaris and Linux. This fouled up a script I was running today on
Linux, so I went to investigate and wrote that short piece of C
shown above. It showed a lot of times that times are different.
I could only test Linux tonight (kernel 2.4.13, glibc 2.2.3).
I might have one or more of the BSDs running this weekend to do
other tests. But I wanted to get an idea of what/why for now.

--
-----------------------------------------------------------------
| Phil Howard - KA9WGN | Dallas | http://linuxhomepage.com/ |
| phil-...@ipal.net | Texas, USA | http://phil.ipal.org/ |
-----------------------------------------------------------------

Andrew Gierth

unread,
Nov 4, 2001, 8:54:54 AM11/4/01
to
>>>>> "phil" == phil-news-nospam <phil-new...@ipal.net> writes:

phil> Under what conditions should the following produce output:

phil> gettimeofday( & tod, NULL );
phil> tim = time( NULL );
phil> if ( tim < tod.tv_sec ) {
phil> printf( "gettimeofday() = [%lu, %lu], time() = %lu\n",

never, unless the system clock is changed.

phil> I would think that the possible range of values for time()
phil> would be anywhere from the current time truncated to the
phil> nearest second to the current time rounded to the nearest
phil> second. Since time() is called after gettimeofday() I would
phil> think it could not be less. However, this seems to happen a
phil> lot. I've seen this happen in both Solaris and Linux. This
phil> fouled up a script I was running today on Linux, so I went to
phil> investigate and wrote that short piece of C shown above. It
phil> showed a lot of times that times are different.

I tried this on Solaris 7 and FreeBSD. On Solaris, my conclusion is
that time() and gettimeofday() are not actually reading the same
clock, and that the time skew between the two is variable either
between systems or slowly over time (on the machines I tried, time()
was updating between 1 and 6ms later than gettimeofday, but the
difference was fairly stable over the short term). I also checked
clock_gettime(CLOCK_REALTIME), which on Solaris seems to be reading
the same clock as gettimeofday().

On FreeBSD time() calls gettimeofday() anyway, so the results are
always the same.

--
Andrew.

comp.unix.programmer FAQ: see <URL: http://www.erlenstar.demon.co.uk/unix/>
or <URL: http://www.whitefang.com/unix/>

phil-new...@ipal.net

unread,
Nov 4, 2001, 9:51:15 PM11/4/01
to
On 04 Nov 2001 13:54:54 +0000 Andrew Gierth <and...@erlenstar.demon.co.uk> wrote:
|>>>>> "phil" == phil-news-nospam <phil-new...@ipal.net> writes:

| phil> Under what conditions should the following produce output:
|
| phil> gettimeofday( & tod, NULL );
| phil> tim = time( NULL );
| phil> if ( tim < tod.tv_sec ) {
| phil> printf( "gettimeofday() = [%lu, %lu], time() = %lu\n",
|
| never, unless the system clock is changed.

frequent, is what actually happens.


| I tried this on Solaris 7 and FreeBSD. On Solaris, my conclusion is
| that time() and gettimeofday() are not actually reading the same
| clock, and that the time skew between the two is variable either
| between systems or slowly over time (on the machines I tried, time()
| was updating between 1 and 6ms later than gettimeofday, but the
| difference was fairly stable over the short term). I also checked
| clock_gettime(CLOCK_REALTIME), which on Solaris seems to be reading
| the same clock as gettimeofday().
|
| On FreeBSD time() calls gettimeofday() anyway, so the results are
| always the same.

As explained in my post on comp.os.linux.development.system I found what
the Linux (2.4.13) kernel was doing. While gettimeofday() was getting
the current clock, time() was taking a shortcut and grabbing an already
stored value. I didn't track down where this value got stored, but my
guess is it was from a clock interrupt. If that is so, it could be low
by up to as much as the interval between those events. In fact I found
that the largest difference gettimeofday() had over time() was the one
second offset plus an additional 8.128ms. Apparently a clock event
could be at N+0.999999, store the value N, then be off by 1 until the
next clock event.

If everything used the same value, I wouldn't have seen a problem. But
different calls use, effectively, different time sources, so there is a
real problem there if they are not in exact sync.

Anyway, I made this patch to the Linux 2.4.13 kernel:
http://phil.ipal.org/fix-time-2.4.13.patch
and am running it now. Now the test program I originally posted no
longer gives any output, as expected. I don't see this as wrong since
as you mention, FreeBSD time() calls gettimeofday(). The Linux kernel
also has comments suggesting this be done in userspace. I did it as a
kernel hack rather than a libc hack, because I'm not as comfortable
rebuilding libc, yet.

Anyway, that time difference is enough that sometimes even on the scale
of a shell script, time can appear to occaisionally jump backwards by a
second. It has impacted some scripts of mine a few times over the years.

Could Solaris possibly be doing something similar by handling the time()
syscall by grabbing a time value stored a few milliseconds earlier?

The extended time and calendar functions I have in a big general purpose
library I'm building (to be released) now use gettimeofday() exclusively
for time values, and never time(), to be sure I get something consistent
on all platforms.

stanislav shalunov

unread,
Nov 5, 2001, 12:44:50 PM11/5/01
to
Andrew Gierth <and...@erlenstar.demon.co.uk> writes:

> On FreeBSD time() calls gettimeofday() anyway, so the results are
> always the same.

While time() does call gettimeofday(), the phenomenon that the
original poster has observed on Linux is present on my FreeBSD
3.4-RELEASE machine. Running his program, I do see output:

gettimeofday() = [1004980190, 210942], time() = 1004980189
gettimeofday() = [1004980266, 292483], time() = 1004980265
gettimeofday() = [1004980266, 442487], time() = 1004980265
gettimeofday() = [1004980282, 302846], time() = 1004980281
gettimeofday() = [1004980282, 632853], time() = 1004980281
gettimeofday() = [1004980309, 403399], time() = 1004980308
gettimeofday() = [1004980328, 513768], time() = 1004980327
gettimeofday() = [1004980339, 133943], time() = 1004980338
gettimeofday() = [1004980377, 24555], time() = 1004980376
^C

This seems reproduceable.

This is quite inexplicable given that /usr/src/lib/libc/gen/time.c
defines time() as follows:

#include <sys/types.h>
#include <sys/time.h>

time_t
time(t)
time_t *t;
{
struct timeval tt;

if (gettimeofday(&tt, (struct timezone *)0) < 0)
return(-1);
if (t)
*t = tt.tv_sec;
return(tt.tv_sec);
}

This machine does run NTP. There were no time reset events in the
interval of time when the observation occured (and NTP is quite happy
reporting 230us jitter to the source it synchronized to).

I have tried running the following code to test gettimeofday()
monotonicity without time():

#include <stdio.h>
#include <sys/time.h>

int
main()
{
struct timeval old, new;
old.tv_sec = old.tv_usec = 0;
for (;;) {
gettimeofday(& new, NULL);
if ((new.tv_sec < old.tv_sec) ||
((new.tv_sec == old.tv_sec) && (new.tv_usec < old.tv_usec)))
printf("old timeval = [%li, %li], new timeval = [%li, %li]\n",
old.tv_sec, old.tv_usec, new.tv_sec, new.tv_usec);
old.tv_sec = new.tv_sec;
old.tv_usec = new.tv_usec;
}
}

Notice that I'm printing timeval members as signed long values,
according to their definition in /usr/include/sys/time.h:

struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* and microseconds */
};

This monotonicity test is producing output too:

old timeval = [1004981814, 954993], new timeval = [1004981814, -694471210]
old timeval = [1004981850, 235728], new timeval = [1004981849, -694190475]
old timeval = [1004981850, 645735], new timeval = [1004981849, -693780466]
^C

It looks like something in the kernel isn't quite consistent about
whether timeval members are long or unsigned long. Given that tv_usec
should only change within 0..999999 diapason it shouldn't even matter
much, but apparently it does.

At any rate, at least on FreeBSD this phenomenon appears to be related
to gettimeofday() behavior and time(), being a simple wrapper, is OK
(assuming tv_usec never goes above 999999).

--
Stanislav Shalunov http://www.internet2.edu/~shalunov/

This message is designed to be viewed at 600 dpi.

Norman Black

unread,
Nov 5, 2001, 4:13:47 PM11/5/01
to
gettimeofday on Linux uses the rdtsc instruction and has higher resolution. It
is likely the time API call is not using gettimeofday in its implementation.

In this case gettimeofday can register the small time differences in between the
two "ticks" of the counter used for time (likely the system clock tick, usually
around 10ms). In this case time can register a lower value than the gettimeofday
API call even if called after gettimeofday.

--
Norman Black
Stony Brook Software
nospam => stonybrk

<phil-new...@ipal.net> wrote in message
news:9s2hh...@enews3.newsguy.com...

Casper H.S. Dik - Network Security Engineer

unread,
Nov 5, 2001, 5:26:02 AM11/5/01
to
[[ PLEASE DON'T SEND ME EMAIL COPIES OF POSTINGS ]]

Caspe...@Holland.Sun.Com (Casper H.S. Dik - Network Security Engineer) writes:


>It appears to be a bug in at least Solaris 7and 8 (not 9)


The bug id is:

4322645 gettimeofday() not behaving as manpage suggests

(fixed in Solaris 9)

Casper
--
Expressed in this posting are my opinions. They are in no way related
to opinions held by my employer, Sun Microsystems.
Statements on Sun products included here are not gospel and may
be fiction rather than truth.

Casper H.S. Dik - Network Security Engineer

unread,
Nov 4, 2001, 12:20:06 PM11/4/01
to
[[ PLEASE DON'T SEND ME EMAIL COPIES OF POSTINGS ]]

Andrew Gierth <and...@erlenstar.demon.co.uk> writes:

>>>>>> "phil" == phil-news-nospam <phil-new...@ipal.net> writes:

> phil> Under what conditions should the following produce output:

> phil> gettimeofday( & tod, NULL );
> phil> tim = time( NULL );
> phil> if ( tim < tod.tv_sec ) {
> phil> printf( "gettimeofday() = [%lu, %lu], time() = %lu\n",

>never, unless the system clock is changed.

It appears to be a bug in at least Solaris 7and 8 (not 9)

Casper

phil-new...@ipal.net

unread,
Nov 5, 2001, 9:00:26 PM11/5/01
to
On Mon, 5 Nov 2001 13:13:47 -0800 Norman Black <nos...@ix.netcom.com> wrote:
| gettimeofday on Linux uses the rdtsc instruction and has higher resolution. It
| is likely the time API call is not using gettimeofday in its implementation.

Indeed it is not. I patched it in the kernel to have time() do the same
same thing as gettimeofday() at the syscall level. That fixed the error
I saw. My patch is: http://phil.ipal.org/fix-time-2.4.13.patch

Perry L. Rose

unread,
Nov 6, 2001, 5:26:32 PM11/6/01
to

I tried this program on an SGI Challenge L box running IRIX 6.5 and it
produced no output whatsoever (as expected). This machine has a fairly
precise realtime clock (although I cannot remember what that precision
is :-). I doubt that the realtime clock precision has anything to do
with the fact that time() and gettimeofday() always produce the same
second count, however. The consistency is probably due to both
functions accessing the same kernel memory location and doing so in
exactly sequential order. I plan on trying this out on a different
versions of Linux in order to see if they all behave consistently.

0 new messages