2012-11-04 14 views
9

Si supponga di disporre di uno scheduler cooperativo in un ambiente incorporato. Ho molti processi in esecuzione. Voglio utilizzare il timer del watchdog in modo da poter rilevare quando un processo ha smesso di comportarsi per qualsiasi motivo e ripristinare il processore.Come utilizzare il timer del watchdog in un RTOS?

Nelle applicazioni più semplici senza RTOS toccherei sempre il watchdog dal loop principale e questo era sempre adeguato. Tuttavia, qui ci sono molti processi che potrebbero potenzialmente bloccarsi. Qual è un metodo pulito per toccare periodicamente il timer del watchdog, assicurando che ogni processo sia in buona salute?

Stavo pensando che potrei fornire una funzione di callback a ogni processo in modo che possa lasciare che un'altra funzione, che supervisiona tutto, sappia che è ancora viva. Il callback passerebbe un parametro che sarebbe l'id univoco delle attività in modo che il sorvegliante potesse determinare chi stava richiamando.

+1

Stiamo prendendo in considerazione un watchdog che fa parte di RTOS o un timer di watchdog hardware effettivo che i servizi RTOS? –

risposta

13

Un approccio comune è quello di delegare il cane da guardia calci ad un compito specifico (spesso sia la più alta priorità o la priorità più bassa, Compromessi/motivazioni per ogni approccio), e poi tutti gli altri compiti "check-in" con questo compito.

questo modo:

  • se un interrupt è appeso (100% CPU), il compito kicker non funzionerà, si ripristina

  • se il compito kicker è appeso, si ripristina

  • se un altro compito è appeso, compito kicker non vede il check-in, il compito kicker non calciare WDG, si ripristina

Ora ci sono naturalmente dettagli di implementazione da considerare. Alcune persone hanno ogni compito impostato il proprio bit dedicato (atomicamente) in una variabile globale; l'attività kicker controlla questo gruppo di bit flag ad una velocità specifica e cancella/reimposta quando tutti hanno effettuato il check-in (insieme al kicking del WDG, ovviamente.) Evito i globals come la peste ed evito questo approccio. I flag di eventi RTOS forniscono un meccanismo un po 'simile che è più elegante.

In genere progetto i miei sistemi embedded come sistemi event-driven. In questo caso, ogni attività si blocca in un posto specifico - in una coda di messaggi. Tutte le attività (e ISR) comunicano tra loro inviando eventi/messaggi. In questo modo, non devi preoccuparti di un'attività che non si verifica perché è bloccata su un semaforo "Laggiù" (se ciò non ha senso, scusa, senza scrivere molto di più non riesco a spiegarlo meglio).

Inoltre, vi è la considerazione: le attività eseguono il check-in "autonomamente" o rispondono/rispondono a una richiesta dall'attività kicker. Autonomo - ad esempio, una volta al secondo, ogni attività riceve un evento nella sua coda "tell task kicker sei ancora vivo".Richiesta di risposta: una volta al secondo (o qualsiasi altra cosa), le attività di kicker indicano a tutti (tramite le code) "il tempo di effettuare il check-in" - e alla fine ogni attività esegue la coda, ottiene la richiesta e le risposte. Si applicano considerazioni su priorità del compito, teoria delle code, ecc.

Ci sono 100 modi per skinare questo gatto, ma il principio di base di una singola attività che è responsabile del kicking del WDG e del fatto che altre attività si incanalano verso l'attività kicker è piuttosto standard.

C'è almeno un altro aspetto da considerare - al di fuori dello scopo di questa domanda - e che si occupa degli interrupt. Il metodo descritto sopra attiverà il reset di WDG se un ISR esegue il hogging della CPU (buono), ma per quanto riguarda lo scenario opposto - un ISR (purtroppo) si è disabilitato accidentalmente e inavvertitamente. In molti scenari, questo non verrà rilevato e il tuo sistema continuerà a battere il WDG, tuttavia parte del tuo sistema è paralizzata. Cose divertenti, ecco perché amo lo sviluppo embedded.

1

Il metodo tradizionale è quello di avere un processo watchdog con la priorità più bassa possibile

PROCESS(watchdog, PRIORITY_LOWEST) { while(1){reset_timer(); sleep(1);} } 

E dove il timer hardware effettivo resetta la CPU ogni 3 o 5 secondi forse.

Il tracciamento dei singoli processi può essere ottenuto mediante logica inversa: ogni processo configurerebbe un timer il cui callback invia al watchdog un messaggio di "stop". Quindi ogni processo dovrebbe annullare l'evento del timer precedente e configurarne uno nuovo da qualche parte nel ciclo 'ricezione evento/messaggio dalla coda'.

PROCESS(watchdog, PRIORITY_LOWEST) { 
    while(1) { 
     if (!messages_in_queue()) reset_timer(); 
     sleep(1); 
    } 
} 
void wdg_callback(int event) { 
    msg = new Message(); 
    send(&msg, watchdog); 
}; 
PROCESS(foo, PRIORITY_HIGH) { 
    timer event=new Timer(1000, wdg_callback); 
    while (1) { 
     if (receive(msg, TIMEOUT)) { 
      // handle msg  
     } else { // TIMEOUT expired 
      cancel_event(event); 
      event = new Timer(1000,wdg_callback); 
     } 
    } 
} 
+0

Ma il problema con questo approccio è che accetterebbe solo un problema se il processo di watchdog fosse affamato o se ci fosse un problema più grande con RTOS. Non avrebbe problemi con nessun particolare processo. O mi sta sfuggendo qualcosa? – user946230

+0

@ user946230: no hai ragione. Il problema è affrontato nell'aggiornamento. Questo riduce il numero di messaggi inviati. Inoltre è possibile codificare le funzioni del watchdog all'interno del processo 'idle', che in genere ha il PID = 0 e contemporaneamente catturare il caso più tipico di messaggi persi. –

+0

Bene, l'inattività non riesce a dormire. ma altrimenti.... –

1

Una soluzione modello:

  • Ogni filo che vuole essere controllato registra esplicitamente la sua richiamata con il filo cane da guardia, che mantiene un elenco di tali callback.
  • Quando il watchdog è programmato, può iterare l'elenco delle attività registrate
  • Ogni callback stesso viene chiamato iterativamente finché non restituisce uno stato integro.
  • Alla fine della lista viene eseguito il kicker del watchdog hardware.

In questo modo qualsiasi thread che non restituisce mai uno stato integro interromperà l'attività del watchdog fino a quando non si verifica il timeout del watchdog hardware.

In un sistema operativo preemptive, il thread del watchdog sarebbe la priorità minima o il thread inattivo. In uno scheduler cooperativo, dovrebbe produrre tra le chiamate di richiamo.

Il design delle funzioni di callback dipende dal compito specifico e dal suo comportamento e periodicità. Ogni funzione può essere adattata alle esigenze e alle caratteristiche del compito. Compiti di elevata periodicità potrebbero semplicemente incrementare un contatore, che viene impostato a zero quando viene richiamato il callback. Se il contatore è zero in entrata, l'attività non ha programmato dall'ultimo controllo del watchdog. Le attività con un comportamento basso o aperiodico potrebbero temporalizzare la pianificazione, pertanto la richiamata potrebbe restituire un errore se l'attività non è stata pianificata per un determinato periodo di tempo. Sia le attività che i gestori di interrupt potrebbero essere monitorati in questo modo. Inoltre, poiché è responsabilità di un thread registrarsi con il watchdog, potresti avere alcuni thread che non si registrano affatto.

0

Ogni attività deve avere un proprio cane da guardia simulato. E il vero cane da guardia è alimentato da un task in tempo reale ad alta priorità solo se tutti i watchdog simulati non hanno timeout.

cioè:

void taskN_handler() 
{ 
    watchdog *wd = watchdog_create(100); /* Create an simulated watchdog with timeout of 100 ms */ 
    /* Do init */ 
    while (task1_should_run) 
    { 
     watchdog_feed(wd); /* feed it */ 
     /* do stuff */ 
    } 
    watchdog_destroy(wd); /* destroy when no longer necessary */ 
} 

void watchdog_task_handler() 
{ 
    int i; 
    bool feed_flag = true; 
    while(1) 
    { 
     /* Check if any simulated watchdog has timeout */ 
     for (i = 0; i < getNOfEnabledWatchdogs(); i++) 
     { 
      if (watchogHasTimeout(i)) { 
        feed_flag = false; 
        break; 
      } 
     } 

     if (feed_flag) 
      WatchdogFeedTheHardware(); 

     task_sleep(10); 
} 

Ora, si può dire che il sistema è davvero protetto, non ci saranno si blocca, si blocca nemmeno parziali, e soprattutto, senza innesco watchdog indesiderati.

0

Altre risposte hanno coperto la tua domanda, vorrei solo suggerirti di aggiungere qualcosa nella tua vecchia procedura (senza RTOS). Non calciare il cane da guardia incondizionatamente solo dal main(), è possibile che qualche ISR sia bloccato, ma il sistema continuerà a funzionare senza preavviso (il problema che Dan ha menzionato riguarda anche RTOS).

Quello che ho sempre fatto è stato collegare l'interrupt principale e il timer in modo che all'interno del timer sia stato fatto un conto alla rovescia su una variabile fino a quando era zero, e dal main avrei controllato se era zero, e solo quindi dare da mangiare al cane da guardia. Ovviamente, dopo l'alimentazione, restituisci la variabile al valore iniziale. Semplice, se la variabile ha smesso di decrementare, si ottiene il reset. Se stop principale alimenta il watchdog, si ottiene il reset.

Questo concetto è facile da applicare solo per eventi periodici noti, ma è comunque meglio fare tutto solo dal principale. Un altro vantaggio è che il codice alterato non è così propenso a calciare il cane da guardia perché la procedura di feed watchdog all'interno del main è terminata all'interno di un loop selvaggio.

Problemi correlati