[EDIT] Un'importante riscrittura con riferimenti poiché ho appena annotato la vecchia risposta a memoria.
Risposta breve:no. Al giorno d'oggi non è possibile ottenere una precisione vicina al millisecondo da un sistema operativo ordinario su una piattaforma x86/x64.
ESCLUSIONE DI RESPONSABILITÀ Questa è una risposta da profani poiché sono un normale amministratore di sistema con una normale visione dei computer da amministratore di sistema. È probabile che alcuni sviluppatori di kernel e architetti hardware abbiano un livello professionale di conoscenza del cronometraggio.
Risposta lunga:
Bisogna pur cominciare da qualche parte. Lo farò dall'alto verso il basso, iniziando con le applicazioni che si spostano verso il basso verso gli oscillatori.
Il primo problema non è avere il cronometraggio su un computer, ma riuscire a far sì che l'ambiente nel suo insieme sia d'accordo su qualunque cronometraggio tu abbia. Che cronometraggio? Si scopre che ci sono un paio di modi per tenere il tempo in un computer di oggi. Quello che vediamo di più è l'ora del sistema (come mostrato in uno degli angoli dello schermo). Iniziamo facendo finta che sia così semplice e complichiamo le cose un paio di paragrafi più in basso.
Vogliamo che l'ora del sistema sia corretta e vogliamo che sia uniforme su tutti i nostri computer. Abbiamo bisogno di un modo per comunicarlo da una fonte attendibile a un livello così granulare in modo da soddisfare i nostri requisiti, qualunque essi siano.
Trasformiamo il nostro requisito in un livello di tolleranza di 1 ms, ovvero il nostro tempo può deviare di 1 ms all'interno del nostro ambiente o perdiamo un obiettivo critico. Diamo un'occhiata concreta e vediamo cosa può fare Microsoft per noi.
Escludendo obsoleti come NT, Windows nativo esegue il cronometraggio in base a ntp semplificato (computer aggiunti al dominio che iniziano con XP/2003) o sntp semplificato (computer non aggiunti al dominio che iniziano con Win2k) - grazie a @Ryan per aver individuato questo dettaglio . Microsoft ha fissato due obiettivi durante l'implementazione del cronometraggio, nessuno dei quali include il livello di precisione desiderato:
"Non garantiamo e non supportiamo l'accuratezza del servizio W32Time tra i nodi su una rete. Il servizio W32Time non è una soluzione NTP completa che soddisfa le esigenze delle applicazioni sensibili al tempo. Il servizio W32Time è progettato principalmente per fare quanto segue:
- Fai funzionare il protocollo di autenticazione Kerberos versione 5.
- Fornisci tempi di sincronizzazione liberi per i computer client.
Il servizio W32Time non è in grado di mantenere in modo affidabile il tempo di sincronizzazione nell'intervallo da uno a due secondi. Tali tolleranze sono al di fuori delle specifiche di progettazione del servizio W32Time."
OK. Supponendo che stiamo eseguendo il tuo stack di servizi su più di un computer e abbiamo un livello di tolleranza del cronometraggio che si avvicina a 1 ms per la correlazione degli eventi, è piuttosto una delusione. Se lo stack di servizi include due computer, in realtà non possiamo utilizzare il cronometraggio nativo di Windows. Ma già che ci siamo, sottolineiamo uno o due punti chiave sul cronometraggio nativo di Windows e includiamo una documentazione completa:
Se disponi di un AD, osserva che l'ora in un determinato dominio verrà sincronizzata dal ruolo di emulatore PDC, a seconda di quale DC lo abbia. L'inserimento dell'ora corretta nel dominio deve quindi avvenire tramite il controller di dominio che esegue il ruolo di emulatore PDC. Se in una foresta multidominio questo si traduce nell'emulatore PDC del dominio radice della foresta. Da lì il tempo viene distribuito principalmente agli emulatori PDC dei sottodomini ea ciascun membro del dominio in modo fan out (con alcuni avvertimenti). Questo processo è documentato qui. Informazioni ancora più approfondite qui
OK. Cosa possiamo fare?
Per cominciare, abbiamo bisogno di un modo più preciso per sincronizzare il tempo in tutto l'ambiente. Supponendo che non possiamo eseguire Linux ntpd o ntpd per Windows, potresti dare un'occhiata a un client shareware chiamato Tardis, ma probabilmente ce ne sono molti altri là fuori da provare.
Abbiamo eseguito Tardis su un server Win2k3 in esecuzione come emulatore PDC che aveva un orologio CMOS con un'inclinazione davvero ampia, per ragioni storiche inspiegabili non avevamo altra scelta che sincronizzare l'intera rete da esso. Ora è stato sostituito con grande gioia da un Linux ntpd dedicato che porta il tempo dagli orologi atomici all'esterno, ma Tardis ci ha salvato in modo ammirevole lì per lì. Non so tuttavia se potrebbe aiutarti a ottenere una precisione maggiore rispetto a quella nativa di Windows.
Ma supponiamo da questo punto in poi che noi (noi) abbiamo capito come implementare una perfetta sincronizzazione dell'ora di rete sostitutiva. Grazie alla sua intrinseca astuzia, ha una capacità di tollerare livelli inferiori a un millisecondo. L'abbiamo messo in atto in modo da imporre il modo in cui il nostro AD si aspetta che il tempo si diffonda attraverso la rete.
Questo significa che possiamo ottenere una diagnostica accurata dai sistemi operativi e dai microservizi con una granularità che si avvicina a singoli millisecondi?
Diamo un'occhiata a come i sistemi operativi sull'architettura x86/x64 pianificano il tempo del processore.
Usano interruzioni, che sono bestie sfaccettate ricche di sostanza archeologica. Tuttavia, il sistema operativo non è solo nel suo desiderio di interrompere. Anche l'hardware desidera interrompere e ha i mezzi per farlo! (Ciao tastiera) E i sistemi operativi stanno al gioco.
È qui che diventa complicato e lo risolverò semplificando eccessivamente. Domande? Mi chino, copro e vi indico un trattato assolutamente eccellente sull'argomento. (Se stai cercando millisecondi su una piattaforma Windows, dovresti davvero leggerlo...) Una versione aggiornata per Win8.1/Win2012r2 è in lavorazione, ma non è ancora emersa alcuna data di rilascio.
OK, interrompe. Ogni volta che qualcosa deve accadere in un sistema operativo, un'interruzione attiva l'azione che segue. L'azione è un mucchio di istruzioni recuperate dal kernel, che possono essere eseguite in molti modi diversi. La linea di fondo è che nonostante l'interruzione avvenga in un momento che può essere determinato con maggiore o minore precisione a seconda dell'architettura hardware e della gestione dell'interruzione del kernel, l'ora esatta in cui si verificano le parti successive dell'esecuzione generalmente non può. Un set specifico di istruzioni può essere eseguito subito dopo l'interruzione o in ritardo, può essere eseguito in una sequenza prevedibile o meno, può essere vittima di hardware difettoso o driver scritti male che influenzano latenze difficili da riconoscere. Il più delle volte uno semplicemente non lo sa. Il timestamp a livello di millisecondi che viene mostrato nel file di log successivo:è molto preciso, ma è preciso su quando si è verificato l'evento?
Fermiamoci brevemente con l'interruzione del cronometraggio. Un interrupt viene fornito con un livello di priorità, il livello più basso è dove le applicazioni utente (come un servizio standard) ottengono il tempo del processore. Gli altri livelli (superiori) sono riservati all'hardware e al lavoro del kernel. Se arriva un'interruzione a un livello superiore al più basso, il sistema farà finta che non esistano interruzioni con priorità inferiore anche in coda (fino a quando le interruzioni con priorità più alta non sono state curate). Le normali applicazioni e servizi in esecuzione saranno in questo modo gli ultimi in fila per il tempo del processore. Al contrario, viene data quasi la massima priorità all'interrupt di clock. L'aggiornamento dell'ora verrà quasi sempre eseguito in un sistema. Questa è una semplificazione eccessiva quasi criminale di come funziona, ma serve allo scopo di questa risposta.
Il tempo di aggiornamento consiste in realtà di due attività:
-
Aggiornamento dell'ora di sistema / AKA l'orologio da parete / AKA quello che dico quando qualcuno mi chiede che ore sono / AKA la cosa ntp giocherella un po' avanti e indietro rispetto ai sistemi vicini.
-
Aggiornamento del conteggio dei tick, utilizzato ad esempio per misurare le durate nell'esecuzione del codice.
Ma che si tratti di wall time o conteggio dei tick, da dove prende il tempo il sistema? Dipende molto dall'architettura hardware. Da qualche parte nell'hardware uno o più oscillatori stanno ticchettando e quel ticchettio viene portato attraverso uno dei diversi percorsi possibili in un'interfaccia per il contatto con il kernel mentre aggiorna con maggiore o minore precisione e accuratezza il suo tempo di parete e il conteggio dei tick.
Esistono diversi modelli di progettazione per il posizionamento dell'oscillatore in un sistema multicore, il principale elemento di differenziazione sembra essere il posizionamento sincrono rispetto a quello asincrono. Questi insieme alle rispettive sfide per un cronometraggio accurato sono descritti qui per esempio.
In breve, il cronometraggio sincrono ha un clock di riferimento per multicore, che distribuisce il segnale a tutti i core. Il cronometraggio asincrono ha un oscillatore per core. Vale la pena notare che gli ultimi processori multicore Intel (Haswell) utilizzano una qualche forma di progettazione sincrona utilizzando un bus seriale chiamato "QuickPath Interconnect" con "Forwarded Clocking", rif. scheda dati. Il Forwarded Clocking è descritto in termini tali che un profano (io) può capirlo rapidamente e superficialmente qui.
OK, quindi con tutto quel nederismo fuori mano (che è servito a dimostrare che il cronometraggio è un compito pratico complesso con un sacco di storia vivente su di esso), diamo un'occhiata ancora più da vicino alla gestione degli interrupt.
I sistemi operativi gestiscono gli interrupt utilizzando una delle due strategie distinte:ticking o tickless. I tuoi sistemi utilizzano l'uno o l'altro, ma cosa significano i termini?
Kernel ticchettanti inviare interrupt a intervalli fissi. Il sistema operativo non è in grado di misurare il tempo con una risoluzione migliore dell'intervallo di tick. Anche in questo caso, l'effettiva elaborazione coinvolta nell'esecuzione di una o più azioni potrebbe contenere un ritardo maggiore dell'intervallo di tick. Considera ad esempio i sistemi distribuiti (come i microservizi) in cui i ritardi inerenti alle chiamate tra servizi potrebbero richiedere molto tempo. Tuttavia, ogni set di istruzioni sarà associato a uno o più interrupt misurati dal sistema operativo con una risoluzione non superiore al tempo di ticking del kernel. Il tempo di tick ha un valore di base ma può essere ridotto almeno in Windows su richiesta da una singola applicazione. Questa è un'azione associata non solo ai benefici ma anche ai costi, e porta con sé un po' di stampa fine.
I cosiddetti tickless kernel (che hanno un nome molto poco descrittivo) sono un'invenzione relativamente nuova. Un kernel tickless imposta il tempo di tick a intervalli variabili (la più lunga durata possibile nel futuro). Il motivo è che il sistema operativo consente dinamicamente ai core del processore di entrare in vari livelli di sospensione il più a lungo possibile, con il semplice scopo di risparmiare energia. "Vari livelli" includono istruzioni di elaborazione alla massima velocità, elaborazione a velocità ridotte (ovvero velocità del processore più lenta) o nessuna elaborazione. Core diversi possono funzionare a velocità diverse e il kernel tickless cerca di lasciare che i processori siano il più inattivi possibile, anche nei casi che includono istruzioni di accodamento per attivarli in batch di interrupt. In breve, diversi core in un sistema multiprocessore possono spostarsi nel tempo l'uno rispetto all'altro. Questo ovviamente crea scompiglio con il buon mantenimento del tempo, ed è finora un problema irrisolto con le nuove architetture di processori a risparmio energetico e i kernel tickless che consentono loro di eseguire un risparmio energetico efficiente. Confronta questo con un kernel ticking (intervallo di tick statico) che riattiva continuamente tutti i core del processore, indipendentemente dal fatto che ricevano o meno lavoro effettivo, e dove il cronometraggio comporta un grado di imprecisione ma in misura relativamente affidabile rispetto ai kernel tickless.
Il tempo di tick standard di Windows, ovvero la risoluzione del sistema, è di 15,6 ms fino a Windows 8/2012, dove il comportamento predefinito è tickless (ma è ripristinabile al ticking del kernel). Credo che il tempo di tick predefinito di Linux dipenda dalla compilazione del kernel, ma questa nicchia è ben al di fuori della mia esperienza (e anche di questa), quindi potresti voler ricontrollare se dipendi da essa. Credo che i kernel Linux siano compilati tickless dalla 2.6.21 e possano essere compilati con vari flag che ottimizzano il comportamento tickless (e di cui ricordo solo alcune varianti di no_hz).
Questo per quanto riguarda i sistemi bare metal. Nei sistemi virtuali peggiora, poiché la contesa tra VM e hypervisor in modi diversi rende estremamente difficile il cronometraggio accurato. Ecco una panoramica per VMware e qui è uno per RHEL KVM. Lo stesso vale per i sistemi distribuiti. I sistemi cloud sono ancora più difficili in quanto non ci avviciniamo nemmeno a vedere gli hypervisor e l'hardware effettivi.
Per concludere, ottenere un tempo preciso da un sistema è un problema a più livelli. Andando ora dal basso verso l'alto da un punto di vista di alto livello, dobbiamo risolvere:Sincronizzazione temporale interna tra l'hardware e il kernel, elaborazione di interrupt e ritardi nell'esecuzione delle istruzioni di cui desideriamo il tempo, se in un ambiente virtuale imprecisioni grazie all'incapsulamento di un secondo livello del sistema operativo, la sincronizzazione dell'ora tra i sistemi distribuiti.
Pertanto, a questo punto della storia dell'informatica non otterremo una precisione al millisecondo da un'architettura x86/x64, almeno non utilizzando nessuno dei comuni sistemi operativi.
Ma quanto possiamo avvicinarci? Non lo so e dovrebbe variare notevolmente tra i diversi sistemi. Ottenere una presa sull'imprecisione nei propri sistemi specifici è un compito arduo. Basta guardare a come Intel suggerisce di eseguire il benchmarking del codice per vedere che i sistemi ordinari, come quelli che mi capita di amministrare, sono decisamente fuori controllo in questa prospettiva.
Non considero nemmeno il raggiungimento di "Tutte le funzionalità di ottimizzazione energetica, tecnologia Intel Hyper-Threading, ridimensionamento della frequenza e modalità turbo sono state disattivate" nei sistemi critici, molto meno armeggiare con i wrapper di codice in C ed eseguire test a lungo termine per ottenere risposte successive. Cerco solo di tenerli in vita e imparare il più possibile su di loro senza disturbarli troppo. Grazie timestamp, so che non posso fidarmi completamente di te, ma so che non mancano molti secondi. Quando l'effettiva precisione al millisecondo diventa importante, una misura non è sufficiente, ma è necessario un numero maggiore di misurazioni per verificare il modello. Cos'altro possiamo fare?
Infine, è interessante osservare come le persone del sistema operativo in tempo reale pensano alla latenza dell'interruzione. C'è anche un'interessante alternativa alla sincronizzazione del tempo in lavorazione, in cui vengono rese pubbliche un bel po' di statistiche, metodologie e white paper interessanti. Aggiungi l'architettura hardware futura e gli sviluppi del kernel a questo e in pochi anni questa cosa dell'accuratezza del cronometraggio potrebbe non essere più un problema del genere. Si può sperare.