Warum wird die von time () angegebene Zeit manchmal 1 Sekunde hinter der Sekundenkomponente von timespec_get () im C-Code angegeben?

12

Der folgende Codeausschnitt:

struct timespec ts;
for (int x = 0; x < 100000000; x++) {
    timespec_get(&ts, TIME_UTC);
    long cTime = (long) time(NULL);
    if (cTime != ts.tv_sec && ts.tv_nsec < 3000000) {
        printf("cTime: %ld\n", cTime);
        printf("ts.tv_sec: %ld\n", ts.tv_sec);
        printf("ts.tv_nsec: %ld\n", ts.tv_nsec);
    }
}

erzeugt diese Ausgabe:

...
cTime: 1579268059
ts.tv_sec: 1579268060
ts.tv_nsec: 2527419
cTime: 1579268059
ts.tv_sec: 1579268060
ts.tv_nsec: 2534036
cTime: 1579268059
ts.tv_sec: 1579268060
ts.tv_nsec: 2540359
cTime: 1579268059
ts.tv_sec: 1579268060
ts.tv_nsec: 2547039
...

Warum die Diskrepanz zwischen cTimeund ts.tv_sec? Beachten Sie, dass das Problem nicht auftritt, wenn die Bedingung in geändert wird ts.tv_nsec >= 3000000. Das Problem beruht darauf, dass Nanosekunden kleiner als 3000000 sind.

Theo d'Or
quelle
Sie müssen genauer über das verwendete Betriebssystem, die Version davon und die Version der verwendeten C-Bibliothek informiert sein.
Ein Programmierer
2
@Someprogrammerdude Linux Debian 8, GCC 6.3.0.
Theo d'Or
Was ist timespec_get()? Ist das C oder C ++? Sieht aus wie std::timespec_get. Bitte verwenden Sie das entsprechende Tag.
Marco Bonelli
@MarcoBonelli: Es wurde in C11 zu C hinzugefügt. Kann online reproduzieren .
ShadowRanger
@ShadowRanger Vielen Dank für die Referenz, ich konnte keinen manEintrag für timespec_getauf meinem System sehen, also bin ich zu Schlussfolgerungen gesprungen. Macht Sinn.
Marco Bonelli

Antworten:

11

Der Grund ist, dass Sie (implizit) verschiedene Systemuhren verwenden. timespec_get()verwendet die hochauflösende systemweite Echtzeituhr, während time()die grobe Echtzeituhr verwendet wird.

Versuchen zu benutzen

clock_gettime(CLOCK_REALTIME_COARSE, &ts);

Anstelle von dir timespec_get()sollte der Unterschied verschwinden.

Bearbeiten:

Dies ist in der Linux-Kernel-Quelle vclock_gettime.c zu sehen

In der Tat ist das Problem hier etwas subtil zu sehen. Der Sekunden-Teil der Strukturelemente, die von identischen Werten verwendet werden CLOCK_REALTIME_COARSEund diese CLOCK_REALTIMEenthalten, aber der Nanosekunden-Teil ist unterschiedlich. mit CLOCK_REALTIMEkann sie größer sein als 1000000000(die eine Sekunde). In diesem Fall wird es beim Anruf behoben:

ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
ts->tv_nsec = ns;

Diese Korrektur wird weder mit CLOCK_REALTIME_COARSEnoch mit durchgeführt time(). Dies erklärt den Unterschied zwischen CLOCK_REALTIMEund time().

Ctx
quelle
Ist dies irgendwo dokumentiert oder nur ein Artefakt der timeImplementierung mit der (vermutlich) leistungsfähigeren, aber weniger genauen Uhr (nach der Theorie, dass es sowieso nur eine zweite Granularität gibt, also wer braucht Präzision)? Eine Verzögerung von etwa einer Millisekunde in Echtzeit (Online-Tests zeigten eine gelegentliche Verzögerung von mehr als einer ms, aber nicht viel mehr), wenn Sie nur nach einer zweiten Granularität fragen, ist wohl nicht so wichtig.
ShadowRanger
@ShadowRanger Ich habe einige weitere Details hinzugefügt
Ctx
Keine explizite Dokumentation der Absicht, aber das ist genug Detail für eine Abstimmung. :-) Komisch, dass die Uhr tatsächlich zusätzliche Nanosekunden im Wert von mehr als einer Sekunde anzeigen kann.
ShadowRanger
@ShadowRanger Ich konnte keine andere echte Dokumentation als die Quelle dafür finden, was auch bedeutet, dass sich das Verhalten auch ohne vorherige Ankündigung im Detail ändern könnte
Ctx
@ CTX Danke für die ausführliche Antwort! Ich werde timespec_get () anstelle von clock_gettime () verwenden, die Sie empfehlen, da timespec_get () C11 anstelle von POSIX ist und nicht die Einstellung der zu verwendenden Uhr erfordert. Ich hatte keine Ahnung, dass verschiedene Uhren verwendet wurden, aber angesichts der Wahl sehe ich nicht viel Sinn darin, die grobe Uhr zu verwenden.
Theo d'Or