Domanda

Ho bisogno di un tick di timer con una risoluzione di 1ms su Linux. Viene utilizzato per incrementare un valore del timer che a sua volta viene utilizzato per vedere se devono essere attivati ??vari eventi. POSIX timerfd_create non è un'opzione a causa del requisito glibc. Ho provato timer_create e timer_settimer, ma il migliore che ottengo da loro è una risoluzione di 10 ms, i valori più piccoli sembrano predefiniti a una risoluzione di 10 ms. Getittimer e setitimer hanno una risoluzione di 10 ms in base alla manpage.

L'unico modo per eseguire questo timer a cui riesco attualmente a pensare è usare clock_gettime con CLOCK_MONOTONIC nel mio loop principale un test se è passato un ms, e in tal caso per aumentare il contatore (e quindi verificare se i vari eventi dovrebbero essere attivati ).

Esiste un modo migliore per farlo che eseguire una query costante nel ciclo principale? Qual è la soluzione consigliata a questo?

La lingua che sto usando è semplicemente vecchia

Aggiorna
Sto usando un kernel 2.6.26. So che puoi interromperlo a 1kHz, e le funzioni POSIX timer_ * possono quindi essere programmate fino a 1ms ma questo non sembra essere affidabile e non voglio usarlo, perché potrebbe aver bisogno di un nuovo kernel su alcuni sistemi. Alcuni kernel del kernel sembrano avere ancora configurato i 100Hz. E avrei bisogno di rilevarlo. L'applicazione può essere eseguita su qualcosa di diverso dal mio sistema :)

Non riesco a dormire per 1ms perché potrebbero esserci degli eventi di rete a cui devo reagire.

Come l'ho risolto Dato che non è così importante, ho semplicemente dichiarato che il timer globale ha una risoluzione di 100 ms. Tutti gli eventi che utilizzano il proprio timer devono impostare almeno 100 ms per la scadenza del timer. Mi chiedevo più o meno se ci sarebbe stato un modo migliore, quindi la domanda.

Perché ho accettato la risposta Penso che la risposta di freespace abbia meglio descritto perché non è davvero possibile senza un sistema Linux in tempo reale.

È stato utile?

Soluzione

Anche il polling nel ciclo principale non è una risposta: il processo potrebbe non richiedere molto tempo alla CPU, quindi trascorreranno più di 10 ms prima che il codice venga eseguito, rendendolo discutibile.

10ms riguarda la risoluzione del timer standard per la maggior parte dei sistemi operativi in ??tempo reale non ). Ma è discutibile in un non-RTOS: il comportamento dello scheduler e del dispatcher influenzerà notevolmente la velocità con cui è possibile rispondere alla scadenza di un timer. Ad esempio, supponiamo anche che tu abbia un timer con risoluzione inferiore a 10ms, non puoi rispondere alla scadenza del timer se il tuo codice non è in esecuzione. Poiché non è possibile prevedere quando verrà eseguito il codice, non è possibile rispondere con precisione alla scadenza del timer.

Esistono ovviamente kernel Linux in tempo reale, vedi http://www.linuxdevices.com/articles /AT8073314981.html per un elenco. Un RTOS offre servizi in base ai quali è possibile ottenere garanzie soft o hard su quando verrà eseguito il codice. Questo è l'unico modo per rispondere in modo affidabile e preciso alla scadenza dei timer ecc.

Altri suggerimenti

Per ottenere una risoluzione di 1 ms, i timer fanno ciò che libevent .

Organizza i tuoi timer in un min-heap , ovvero la parte superiore dell'heap è il timer con il primo tempo (assoluto) di scadenza (un albero rb funzionerebbe anche ma con un sovraccarico). Prima di chiamare select () o epoll () nel tuo ciclo di eventi principale calcola il delta in millisecondi tra il tempo di scadenza del primo timer e ora. Utilizzare questo delta come timeout per select () . I timeout select () e epoll () hanno una risoluzione di 1ms.

Ho un test di risoluzione del timer che utilizza il meccanismo spiegato sopra (ma non libevente). Il test misura la differenza tra il tempo di scadenza del timer desiderato e la sua scadenza effettiva di timer di 1ms, 5ms e 10ms:

1000 deviation samples of  1msec timer: min=  -246115nsec max=  1143471nsec median=   -70775nsec avg=      901nsec stddev=    45570nsec
1000 deviation samples of  5msec timer: min=  -265280nsec max=   256260nsec median=  -252363nsec avg=     -195nsec stddev=    30933nsec
1000 deviation samples of 10msec timer: min=  -273119nsec max=   274045nsec median=   103471nsec avg=     -179nsec stddev=    31228nsec
1000 deviation samples of  1msec timer: min=  -144930nsec max=  1052379nsec median=  -109322nsec avg=     1000nsec stddev=    43545nsec
1000 deviation samples of  5msec timer: min= -1229446nsec max=  1230399nsec median=  1222761nsec avg=      724nsec stddev=   254466nsec
1000 deviation samples of 10msec timer: min= -1227580nsec max=  1227734nsec median=    47328nsec avg=      745nsec stddev=   173834nsec
1000 deviation samples of  1msec timer: min=  -222672nsec max=   228907nsec median=    63635nsec avg=       22nsec stddev=    29410nsec
1000 deviation samples of  5msec timer: min= -1302808nsec max=  1270006nsec median=  1251949nsec avg=     -222nsec stddev=   345944nsec
1000 deviation samples of 10msec timer: min= -1297724nsec max=  1298269nsec median=  1254351nsec avg=     -225nsec stddev=   374717nsec

Il test è stato eseguito come processo in tempo reale sul kernel Fedora 13 2.6.34, la migliore precisione raggiunta del timer da 1ms era avg = 22nsec stddev = 29410nsec.

Non sono sicuro che sia la soluzione migliore, ma potresti prendere in considerazione la possibilità di scrivere un piccolo modulo del kernel che usi i timer ad alta risoluzione del kernel per eseguire il timing. Fondamentalmente, dovresti creare un file dispositivo per il quale le letture ritornerebbero solo a intervalli di 1 ms.

Un esempio di questo tipo di approccio viene utilizzato nel PBX Asterisk, tramite il modulo ztdummy. Se vai su Google per ztdummy puoi trovare il codice per farlo.

Penso che avrai problemi a ottenere una precisione di 1 ms con Linux standard anche con query costanti nel loop principale, perché il kernel non garantisce che la tua applicazione otterrà CPU in ogni momento. Ad esempio, puoi essere messo a dormire per decine di millisecondi a causa del multitasking preventivo e c'è poco che puoi fare al riguardo.

Potresti voler esaminare Real-Time Linux .

Se si sta prendendo di mira la piattaforma x86, è necessario controllare i timer HPET. Questo è un timer hardware con grande precisione. Deve essere supportato da tuo motherbord (in questo momento lo supportano tutti) e anche il kernel dovrebbe contenere un driver. L'ho usato poche volte senza problemi ed è stato in grado di ottenere una risoluzione molto migliore di 1ms.

Ecco alcuni documenti ed esempi:

Mi sembra di ricordare di aver ottenuto risultati soddisfacenti con il polling basato su gettimeofday / usleep - non avevo bisogno di 1000 timer al secondo o niente, ma avevo bisogno di una buona precisione con i tempi per le zecche di cui avevo bisogno - la mia app era un Controller per drum machine MIDI, e mi sembra di ricordare di avere un'accuratezza inferiore al millisecondo, di cui hai bisogno per una drum machine se non vuoi che suoni come un batterista molto cattivo (specialmente contando le latenze incorporate del MIDI) - iirc (era il 2005, quindi la mia memoria è un po 'sfocata) Stavo arrivando a 200 microsecondi di volte target con addormentato.

Tuttavia, non stavo eseguendo molto altro sul sistema. Se hai un ambiente controllato potresti essere in grado di cavartela con una soluzione del genere. Se c'è altro in corso sul sistema (guarda cron che si avvia aggiornatob, ecc.) Le cose potrebbero andare in pezzi.

Stai usando un kernel Linux 2.4?

Dall'articolo # 1420 di VMware KB ( http://kb.vmware.com/kb/1420 ).

  

Mantengono i sistemi operativi guest Linux   contando il tempo si interrompe.   Kernel 2.4 e precedenti senza patch   programmare il timer del sistema virtuale su   la richiesta si interrompe a 100Hz (100   interrompe al secondo). 2.6 kernel,   d'altra parte, richiedere interrupt   a 1000Hz - dieci volte più spesso. Alcuni   2.4 kernel modificati dai distributori per contenere anche le funzionalità 2.6   richiedere interrupt 1000Hz, o in alcuni   casi, interrompe ad altri tassi, ad esempio   come 512Hz.

Per prima cosa, ottieni il sorgente del kernel e compilarlo con un parametro HZ modificato.

  • Se HZ = 1000 , il timer si interrompe 1000 volte al secondo. È possibile utilizzare HZ = 1000 per una macchina i386.
  • Su una macchina integrata, HZ potrebbe essere limitato a 100 o 200.

Per un buon funzionamento, l'opzione PREEMPT_KERNEL dovrebbe essere attiva. Ci sono kernel che non supportano questa opzione correttamente. Puoi controllarli da la ricerca.

I kernel recenti, ovvero 2.6.35.10, supportano le opzioni NO_HZ, che gira sulle zecche dinamiche. Ciò significa che non ci saranno tick del timer quando è inattivo, ma al momento specificato verrà generato un segno di spunta del timer.

Esiste una patch RT per il kernel, ma il supporto hardware è molto limitato.

Generalmente RTAI è una soluzione all-killer per il tuo problema, ma è sua il supporto hardware è molto limitato. Tuttavia, buoni controller CNC, come emc2, usa RTAI per il suo clock, forse 5000 Hz, ma può essere duro lavoro per installarlo.

Se possibile, è possibile aggiungere hardware per generare impulsi. Ciò farebbe un sistema che può essere adattato a qualsiasi versione del sistema operativo.

Puoi almeno usare nanosleep nel tuo loop per dormire per 1ms? O è una cosa glibc?

Aggiornamento: non importa, vedo dalla pagina man " possono essere necessari fino a 10 ms in più di quanto specificato prima che il processo ritorni di nuovo "

Non è necessario un RTOS per una semplice applicazione in tempo reale. Tutti i processori moderni hanno timer per scopi generici. Ottieni una scheda tecnica per qualsiasi CPU di destinazione su cui stai lavorando. Guarda nel sorgente del kernel, sotto la directory arch troverai il sorgente specifico del processore come gestire questi timer.

Esistono due approcci che puoi adottare con questo:

1) L'applicazione esegue SOLO la macchina a stati e nient'altro. Linux è semplicemente il tuo "boot loader". Crea un oggetto kernel che installa un dispositivo a caratteri. Al momento dell'inserimento nel kernel, imposta GP Timer per l'esecuzione continua. Conosci la frequenza a cui sta funzionando. Ora, nel kernel, disabilita esplicitamente il tuo watchdog. Ora disabilita gli interrupt (hardware E software) Su un kernel Linux a CPU singola, chiamare spin_lock () lo farà (non lasciarlo mai andare.) La CPU è TUA. Ciclo occupato, verifica del valore del GPT fino al superamento del numero di tick richiesto, quando hanno, imposta un valore per il timeout successivo ed inserisci il tuo ciclo di elaborazione. Assicurati solo che il tempo di scoppio del codice sia inferiore a 1 ms

2) Una seconda opzione. Questo presuppone che tu stia eseguendo un kernel Linux preventivo. Imposta un GPT inutilizzato insieme al tuo sistema operativo in esecuzione. Ora, imposta un interrupt per attivare un margine configurabile PRIMA che si verifichi il timeout di 1 ms (diciamo 50-75 uSec.) Quando si attiva l'interrupt, disabiliterai immediatamente gli interrupt e girerai in attesa che si verifichi la finestra di 1ms, quindi accedendo alla macchina a stati e successivamente abilitazione degli interrupt in attesa OUT. Questo spiega il fatto che stai collaborando con ALTRE cose nel kernel che disabilitano gli interrupt. Questo suppone che non vi siano altre attività del kernel che bloccano gli interrupt per lungo tempo (più di 100us.) Ora, puoi MISURARE l'accuratezza del tuo evento di fuoco e allargare la finestra fino a quando non soddisfa le tue necessità.

Se invece stai cercando di imparare come funziona RTOS ... o se stai cercando di risolvere un problema di controllo con più di una responsabilità in tempo reale ... allora usa un RTOS.

Che dire dell'utilizzo di " / dev / rtc0 " (o "quot / / dev / rtc") e la relativa interfaccia ioctl ()? Penso che offra un contatore del timer preciso. Non è possibile impostare la frequenza solo su 1 ms, ma su un valore vicino o 1 / 1024sec (1024Hz) o su una frequenza superiore, come 8192Hz.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top