Il tempo UNIX viene misurato sul tuo computer, con UNIX.
Questa risposta si aspetta che tu sappia che cosa sono il Tempo universale (UTC), il Tempo atomico internazionale (TAI) e il secondo SI. Spiegarli è ben oltre lo scopo di Unix e Linux Stack Exchange. Non si tratta degli scambi di stack di fisica o astronomia.
L'hardware
Il computer contiene vari oscillatori che guidano orologi e timer. Esattamente quello che ha varia da computer a computer a seconda della sua architettura. Ma di solito, e in termini molto generali:
- C'è un timer di intervallo programmabile (PIT) da qualche parte, che può essere programmato per contare un determinato numero di oscillazioni e innescare un interruzione all'unità centrale di elaborazione.
- C'è un contatore di cicli sul processore centrale che conta semplicemente 1 per ogni ciclo di istruzioni che viene eseguito.
La teoria del funzionamento, in termini molto ampi
Il kernel del sistema operativo utilizza PIT per generare tick . Imposta il PIT in modalità di esecuzione libera, contando il giusto numero di oscillazioni per un intervallo di tempo, diciamo, un centesimo di secondo, generando un interrupt e quindi ripristinando automaticamente il conteggio per procedere nuovamente. Vi sono variazioni al riguardo, ma in sostanza ciò provoca un aumento dell'intervallo di tick con una frequenza fissa.
Nel software, il kernel incrementa un contatore ogni tick. Conosce la frequenza di tick, perché ha programmato il PIT in primo luogo. Quindi sa quante zecche compongono un secondo. Può usarlo per sapere quando incrementare un contatore che conta i secondi. Quest'ultima è l'idea del kernel di "UNIX Time". In effetti, conta semplicemente verso l'alto al ritmo di uno al secondo SI se lasciato ai propri dispositivi.
Quattro cose complicano ciò, che presenterò in termini molto generali.
L'hardware non è perfetto. Un PIT la cui scheda tecnica dice che ha una frequenza dell'oscillatore di N Hertz potrebbe invece avere una frequenza (diciamo) di N .00002 Hertz, con ovvie conseguenze.
Questo schema interagisce molto male con la gestione dell'alimentazione, perché la CPU si sta svegliando centinaia di volte al secondo per fare poco più che incrementare un numero in una variabile. Quindi alcuni sistemi operativi hanno quelli che sono noti come design "tickless". Invece di fare in modo che il PIT invii un interrupt per ogni tick, il kernel risolve (dallo scheduler di basso livello) quante tick passeranno senza quanti thread si esauriranno e programma il PIT per contare quel numero di tick nel futuro prima di emettere un interruzione tick. Sa che deve quindi registrare il passaggio di N tick alla successiva interruzione tick, anziché 1 tick.
Il software applicativo ha la capacità di cambiare l'ora corrente del kernel. Può passo il valore o può uccise il valore. La rotazione comporta la regolazione del numero di tick che devono passare per aumentare il contatore dei secondi. Così i secondi contatore non necessariamente contare al ritmo di una al secondo SI comunque , anche ammettendo oscillatori perfette. Lo stepping implica semplicemente la scrittura di un nuovo numero nel contatore dei secondi, che di solito non avverrà fino a 1 secondo SI dall'ultimo secondo spuntato.
I kernel moderni non solo contano i secondi, ma contano anche i nanosecondi. Ma è ridicolo e spesso assolutamente impossibile avere un interruzione tick una volta per nanosecondo. Qui entrano in gioco cose come il contatore del ciclo . Il kernel ricorda il valore del contatore del ciclo ad ogni secondo (o ad ogni tick) e può calcolare, dal valore corrente del contatore quando qualcosa vuole sapere il tempo in nanosecondi, quanti nanosecondi devono essere trascorsi dall'ultimo secondo (o tick). Anche in questo caso, tuttavia, la gestione dell'alimentazione e del calore causa un disastro poiché la frequenza del ciclo di istruzione può cambiare, quindi i kernel fanno cose come fare affidamento su hardware aggiuntivo come (diciamo) un Timer di eventi ad alta precisione (HPET).
Il linguaggio C e POSIX
La libreria standard del linguaggio C descrive tempo in termini di un tipo, opaco time_t
, un tipo di struttura tm
con vari campi specifici, e varie funzioni di libreria come time()
, mktime()
e localtime()
.
In breve: il linguaggio C stesso garantisce semplicemente che time_t
è uno dei tipi di dati numerici disponibili e che l'unico modo affidabile per calcolare le differenze temporali è la difftime()
funzione. È lo standard POSIX che fornisce le garanzie più rigorose che time_t
è in realtà uno dei tipi interi e che conta secondi dall'epoca . È anche lo standard POSIX che specifica il timespec
tipo di struttura.
La time()
funzione è talvolta descritta come una chiamata di sistema. In effetti, al giorno d'oggi non è stata la chiamata di sistema sottostante su molti sistemi per molto tempo. Su FreeBSD, ad esempio, è la chiamata di sistema sottostante clock_gettime()
, che ha vari "clock" disponibili che misurano in secondi o secondi + nanosecondi in vari modi. È questa chiamata di sistema mediante la quale il software applicativo legge UNIX Time dal kernel. (Una clock_settime()
chiamata di sistema corrispondente consente loro di eseguirla e una adjtime()
chiamata di sistema consente loro di spostarla.)
Molte persone agitano lo standard POSIX con affermazioni molto precise ed esatte su ciò che prescrive. Queste persone, il più delle volte, non hanno letto lo standard POSIX. Come enunciato nella sua logica, l'idea di contare "secondi dall'epoca", che è la frase usata dallo standard, non specifica intenzionalmente che i secondi POSIX hanno la stessa lunghezza dei secondi SI, né che il risultato di gmtime()
è "necessariamente UTC, nonostante il suo aspetto ". Lo standard POSIX è intenzionalmenteabbastanza libero da consentire (diciamo) un sistema UNIX in cui l'amministratore va e corregge manualmente le regolazioni del secondo intercalare reimpostando l'orologio la settimana dopo che si verificano. In effetti, la logica sottolinea che è intenzionalmente abbastanza lento da contenere sistemi in cui l'orologio è stato deliberatamente impostato in modo errato su un orario diverso dall'ora UTC corrente.
UTC e TAI
L'interpretazione di UNIX Time ottenuta dal kernel dipende dalle routine di libreria in esecuzione nelle applicazioni. POSIX specifica un'identità tra il tempo del kernel e un "tempo scaduto" in a struct tm
. Ma, come ha sottolineato una volta Daniel J. Bernstein, l'edizione del 1997 dello standard ha ottenuto questa identità imbarazzantemente sbagliata, confondendo la regola dell'anno bisestile del Calendario Gregoriano (qualcosa che gli scolari imparano) in modo che il calcolo fosse errato dal 2100 in poi. "Più onorato nella violazione che nell'osservanza" è una frase che mi viene subito in mente.
E infatti lo è. Diversi sistemi oggi basano questa interpretazione sulle routine di libreria scritte da Arthur David Olson, che consultano il famigerato "database del fuso orario Olson", solitamente codificato in file di database /usr/share/zoneinfo/
. Il sistema Olson aveva due modalità:
- I "secondi dall'epoca" del kernel sono considerati conteggi dei secondi UTC dal 1970-01-01 00:00:00 UTC, fatta eccezione per i secondi bisestili. Questo utilizza il
posix/
set di file di database del fuso orario di Olson. Tutti i giorni hanno 86400 secondi del kernel e non ci sono mai 61 secondi in un minuto, ma non sono sempre la lunghezza di un secondo SI e l'orologio del kernel deve essere ruotato o fatto avanzare quando si verificano i secondi bisestili.
- I "secondi dall'epoca" del kernel sono considerati TAI secondi dal 1970-01-01 00:00:10 TAI. Questo utilizza il
right/
set di file di database del fuso orario di Olson. I secondi del kernel sono lunghi 1 SI secondo e l'orologio del kernel non ha mai bisogno di rotazione o stepping per regolare i secondi bisestili, ma i tempi di rottura possono avere valori come 23:59:60 e i giorni non sono sempre lunghi 86400 secondi del kernel.
M. Bernstein scrisse diversi strumenti, incluso il suo daemontools
set di strumenti, che richiedevano right/
perché semplicemente aggiungevano 10 time_t
per ottenere secondi TAI dal 1970-01-01 00:00:00 TAI. Lo ha documentato nella pagina del manuale.
Questo requisito è stato (forse inconsapevolmente) ereditata dal set di strumenti, come daemontools-encore
e runit
e di Felix von Leitner di libowfat
. Usa Bernsteinmultilog
, Guentermultilog
o Papesvlogd
con una posix/
configurazione Olson , per esempio, e tutti i timestamp TAI64N saranno (al momento della stesura di questo) 26 secondi indietro rispetto al secondo conteggio TAI effettivo dal 1970-01-01 00:00:10 TAI.
Laurent Bercot e io abbiamo affrontato questo problema in s6 e nosh, anche se abbiamo adottato approcci diversi. M. Bercot si tai_from_sysclock()
affida a una bandiera di compilazione. Gli strumenti nosh che si occupano di TAI64N esaminano le variabili di ambiente TZ
e TZDIR
per rilevare automaticamente posix/
e right/
se possono.
È interessante notare che i documenti time2posix()
e le posix2time()
funzioni di FreeBSD che consentono l'equivalente della right/
modalità Olson con time_t
secondi TAI. Apparentemente non sono abilitati, tuttavia.
Di nuovo…
Il tempo UNIX viene misurato sul computer su cui è in esecuzione UNIX, tramite oscillatori contenuti nell'hardware del computer. Non usa secondi SI; non è UTC anche se può assomigliarlo superficialmente; e consente intenzionalmente al tuo orologio di essere sbagliato.
Ulteriori letture