Domanda:
Perché le interruzioni dovrebbero essere brevi in ​​un sistema ben configurato?
oneat
2019-11-26 20:32:09 UTC
view on stackexchange narkive permalink

Perché le interruzioni dovrebbero essere brevi?

Attualmente sto utilizzando STM32 e ha una funzione di prioritizzazione degli interrupt.

Per questo motivo, non vedo alcuna differenza tra queste due opzioni:

  • il ciclo principale e interruzioni
  • un grande interrupt con una priorità molto bassa e l'altro interrupt

Perché dovremmo mantenerli brevi?È a causa della memorizzazione nella cache?Piccola pila?O qualcos'altro?Perché non possiamo semplicemente creare l'intero sistema sugli interrupt e far dormire il ciclo principale?

Qualche idea?

gli IRQ critici sono usati per sincronizzare le attività della macchina a stati, non per fare cose asincrone.Tieni traccia dell'altezza della pila.Se il lavoro supera la potenza di elaborazione UC disponibile, la sincronizzazione viene interrotta per overflow.
Un * interrupt * non può essere breve;è un segnale ad un certo punto nel tempo.Non intendi * "interrupt handling time" * (time), * "interrupt service time" * (time), * "interrupt service routines (ISR)" * (righe di codice, presumibilmente esecuzione veloce), * "ISR"* (righe di codice, presumibilmente in esecuzione veloce) o simili?Puoi risolverlo ([modificando la tua domanda] (https://electronics.stackexchange.com/posts/469121/edit))?
@PeterMortensen: "interrupt" è una scorciatoia abbastanza valida per * gestire * gli interrupt, almeno tra le persone che capiscono cosa significa tutto questo.Il tuo commento è un buon riassunto.Oppure puoi pensare all'interruzione in termini di * interruzione * del codice principale che verrà eventualmente restituito dopo l'ISR.(A meno che non decidiamo di cambiare contesto e tornare a quel punto nello spazio utente solo molto più tardi, allora il tempo ISR rispetto al ritardo per quel compito non è vicino.) Anche il processo di consegna di un interrupt richiede alcuni cicli;potresti semplicemente chiamare quell'inevitabile sovraccarico per un gestore.
Undici risposte:
old_timer
2019-11-26 21:35:36 UTC
view on stackexchange narkive permalink

Non solo non c'è motivo per cui non puoi farlo. Un sistema guidato dagli eventi non è raro. MA. Su molte architetture se ci si trova in un interrupt non è possibile averne un altro, quindi anche un interrupt a bassa priorità può in alcuni sistemi impedire che si verifichi un interrupt a priorità più alta, quindi la regola generale è di entrare e uscire velocemente. Spesso un approccio progettuale è l'interrupt che arriva e fa un po 'di gestione, ma lascia un compito per il kernel o l'attività in primo piano da completare, più visibile con i sistemi operativi.

Credo che il Cortex-M possa annidare le interruzioni, quindi se una si sovrappone a un'altra viene gestita. La maggior parte del chip non è correlata a ARM, quindi non c'è alcun motivo per cui il fornitore del chip non possa mettere un gestore di interrupt davanti al Cortex-M e / o un gestore di priorità, oltre a tutto ciò che è nel braccio fine delle cose.

Tuttavia, è necessario progettare un design basato sulle interruzioni. Come con qualsiasi altro progetto, è necessario eseguire l'ingegneria del sistema. È necessario conoscere ogni possibile interruzione (in situazioni normali, istruzioni indefinite, ecc. Sono fatali) e quanto spesso si verificano. Molti saranno periodi regolari.

Devi anche sapere quanto tempo impiega ogni interruzione e programmarlo in modo tale da assicurarti che tutte le interruzioni vengano gestite in una quantità di tempo che soddisfa le tue specifiche. Ad esempio, un evento di interrupt basato su timer per qualcosa richiede 50 ms per essere gestito normalmente, ma c'è un interrupt casuale non periodico che richiede 20 ms. Sono tollerabili 70 ms per uno di questi? Il chip / core ha una soluzione prioritaria? In caso contrario, hai definito le tue priorità di interruzione in modo tale da poter rispettare i tempi su tutto?

Alla fine della giornata, c'è poca differenza tra tutte le interruzioni e alcune interruzioni e alcune in primo piano rispetto al design e alla tempistica delle interruzioni; hai lo stesso problema.

Quindi la regola generale è rendere gli interrupt snelli, meschini e veloci, ma la realtà è fare l'ingegneria del sistema e sapere quanto tempo hai per ciascuno o gruppi di essi messi insieme.Deve richiedere meno di x quantità di tempo.

Linux chiama il design del primo paragrafo "gestione degli interrupt metà superiore / metà inferiore".La "metà superiore" è il * vero * ISR che potrebbe non parlare nemmeno con l'hardware del dispositivo, ma solo il lavoro in coda per una metà inferiore che viene pianificata come qualsiasi altra attività.
MB.
2019-11-27 06:12:30 UTC
view on stackexchange narkive permalink

Re-entrancy è / diventa un grosso problema quanto più i tuoi ISR ​​sono complessi. La voce di wikipedia ha una descrizione piuttosto succinta di un ISR rientrante:

Un gestore di interrupt rientrante è un gestore di interrupt che riattiva gli interrupt all'inizio del gestore di interrupt. Ciò potrebbe ridurre la latenza degli interrupt. [6] In generale, durante la programmazione delle routine del servizio di interrupt, si consiglia di riattivare gli interrupt il prima possibile nel gestore degli interrupt. Questa pratica aiuta a evitare di perdere interruzioni. [7]

Supponendo che tu non stia scrivendo un sistema operativo generico in cui il rientro potrebbe essere inevitabile, devi tenere presente che l'ulteriore complessità di rientrare nel codice del controller personalizzato potrebbe non valere la pena percepita facilità di scrittura di ISR ​​pigri e di lunga durata.

Ecco un esempio:

  • ISR di lunga durata e priorità bassa inizia a fare qualcosa, chiama una versione di malloc che hai implementato.
  • Chiamata malloc media, vieni interrotto da un thread con priorità più alta: chiama anche malloc
  • scenario a: l'ISR ad alta priorità esce da malloc prima della priorità bassa
  • scenario b: l'ISR ad alta priorità esce da malloc dopo una priorità bassa *

Ok, dirai, metterò un blocco di rotazione su malloc, quindi tutto ciò che devi fare è ripetere la condizione precedente alla primitiva spinlock_aquire che hai creato: è it rientrante?

Dovrei sottolineare qui che semplicemente schiaffeggiare le cose e chiamarlo un giorno è una ricetta per i deadlock basati sull'inversione di priorità. Non è così semplice.

La soluzione del povero uomo a tutti questi problemi è mantenere brevi i tuoi ISR. Ad esempio, nel kernel NT, molto tempo fa (non si è tenuto aggiornato), a qualsiasi IRQL sopra gli ultimi due non era nemmeno permesso guardare la memoria di paging. Questo perché il paging era gestito da un interrupt ...

Quindi la scelta diventa: implementare un meccanismo di accodamento di base e fare in modo che i propri ISR ​​ inviano unità di lavoro, oppure fare in modo che i propri ISR ​​si comportino liberamente ma assicurarsi di avere un ambiente estremamente robusto che non soffocerà su questioni strane (ad es. inversione di priorità).

Se il tuo compito è semplicissimo, come accendere una luce nel tuo drone controllato da arduino, allora vai avanti con tutti i mezzi. Ma se vuoi evitare grattacapi ingegneristici in seguito man mano che il tuo codice diventa più complesso, dovresti davvero evitare il beneficio percepito di non darti alcun vincolo con un ISR.


* chiarimento: lo scenario b non può verificarsi al valore nominale poiché l'ISR con priorità più alta verrà sempre eseguito e completato prima di quello con priorità più bassa. Tuttavia, il percorso del codice preso da uno dei due può essere scambiato. Quindi nella stessa routine malloc , se c'è un accesso a una struttura dati globale, quel codice può essere interrotto in ogni possibile combinazione di modi.

Oltre a questo punto, si dovrebbe affermare che il requisito di rientranza per qualsiasi funzione riguarda l'intero albero delle chiamate. Questo diventa molto difficile da garantire se si finisce per utilizzare librerie di terze parti.

Nota anche: per indirizzare un commento da @ user253751, il re-entrancy non viene risolto semplicemente avvolgendo le cose in un lucchetto. Piuttosto, re-entrancy ha una serie di requisiti che sono ben compresi. Ecco alcuni esempi di codice banali che illustrano la questione.

Si può vedere, osservando questo aspetto, che la scrittura di una funzione di acquisizione di risorse o malloc rientranti diventa molto difficile.

Ma gli ISR rientranti non sono comuni: normalmente devi abilitare esplicitamente gli interrupt negli ISR.
@PeterMortensen Il modello OP "fai tutto in un interrupt e dormi nel ciclo principale" richiede che tu lo faccia
Un blocco non aiuta perché il thread originale non può sbloccare il blocco fino a quando non viene eseguito l'interruzione ad alta priorità, cosa che non sarà finché non ottiene il blocco.
@user253751 Ecco perché ho detto che è una ricetta per deadlock basati sull'inversione di priorità.A proposito, esistono serrature rientranti.(Sono utili per il multi-tasking cooperativo tramite fibre o codice asincrono come futures ecc.)
I lock rientranti non proteggeranno il tuo `malloc` dalla corruzione dell'heap.
Su una macchina single-core, normalmente disabiliteresti semplicemente gli interrupt invece di prendere un blocco effettivo, per rendere atomica una piccola sezione critica.Perché non è solo inversione di priorità, è più simile a una funzione genitore che non può essere eseguita (e sbloccata) finché non hai finito.Quindi impedire che altri ISR vengano eseguiti in momenti inopportuni funziona meglio, a patto che non si lascino gli interrupt disabilitati per troppo tempo.
Ovviamente se progetti attorno alla possibilità di ottenere sezioni critiche all'interno degli ISR (disabilitando gli interrupt), ti divertirai se vuoi usare una CPU multi-core più potente.Con la possibilità di eseguire ISR su entrambi i core contemporaneamente (a meno che non limiti la gestione degli interrupt a un core?) Avrai bisogno di grandi riprogettazioni.
@PeterCordes Spesso non c'è motivo di passare a una CPU multi-core più potente.Non è necessario scrivere il codice per coprire ogni piattaforma hardware immaginabile, come quando si scrive software per PC.
@user253751: Vero, ma è qualcosa di cui essere consapevoli se si desidera riutilizzare parte del codice per un sistema embedded * diverso *.Devi decidere se i vantaggi della semplicità per il progetto corrente superano la riusabilità del codice.Spesso è così perché è un grande vantaggio.
Forse non capisco di cosa stai discutendo (@user253751 e @PeterCodes).Supponendo che non "disabilitiamo solo gli interrupt" (* dato * che la domanda iniziale riguarda gli ISR di lunga durata), il rientro è effettivamente ciò che è necessario per proteggere l'heap.Ho chiarito molto chiaramente che schiacciare un lucchetto e chiamarlo un giorno non era la soluzione, ma anche se * fosse * la soluzione, ora imporrebbe un requisito di rientro nel codice di blocco. @user253751: stai davvero dicendo che i mallocs rientranti non esistono?
Peter Smith
2019-11-26 21:35:03 UTC
view on stackexchange narkive permalink

Per un computer generico, mantenere breve il gestore degli interrupt consente all'elaborazione normale di essere ragionevolmente deterministica, il che può o meno essere un problema a seconda dell'applicazione.

In un difficile processo integrato in tempo reale (in cui il determinismo è di fondamentale importanza) questo ha molto senso.

Ho implementato un sensore di inclinazione molto preciso (2 assi) in cui l'azionamento del sensore era controllato da timer e il loop principale era in modalità sleep fino a quando il contatore non ha attivato l'interruzione per aggiornare quale asse era guidato; anche l ' ADC esterno che stavo utilizzando era controllato da un timer (diverso) per ridurre al minimo il jitter di acquisizione per i campioni medi (che può causare il caos in un sistema di dati campionati).

Per mantenere la capacità di eseguire alcuni calcoli in una finestra di tempo molto ristretta, i gestori di interrupt dovevano essere veloci (prendi l'interrupt, fai il minimo richiesto ed esci).

In altre applicazioni, tutte le cose interessanti possono accadere nei gestori di interrupt (come nel caso in cui ero coinvolto in un sistema di video on demand con fino a 2000 stream simultanei disponibili).

Non esiste una risposta "taglia unica" per i gestori di interrupt e dipende dall'applicazione, anche se, come notato, gli interrupt annidati possono diventare disordinati soprattutto se due gestori devono condividere una singola risorsa (il che può portare alla corruzione dei dati se non viene affrontato correttamente e in un sistema multitasking può anche portare all ' inversione di priorità).

L'approccio da adottare per interrompere i gestori dipende dall'applicazione, ma un primo approccio di "mantenerli semplici e veloci" è sempre un buon punto di partenza.

bta
2019-11-27 05:47:42 UTC
view on stackexchange narkive permalink

Mi sono occupato spesso di questo genere di cose in un progetto precedente, e qui ci sono alcune cose che ho affrontato (spesso imparate nel modo più duro). Alcuni dettagli possono variare in base all'architettura del tuo chip e a quanto sei vicino al bare metal, ma le idee generali sono comunque valide.

  • Nel periodo di tempo prima di cancellare / riattivare l'interruzione, potrebbe attivarsi un'altra interruzione non correlata. Potresti fare la cosa sbagliata servendo un interrupt a bassa priorità quando è in sospeso un interrupt a priorità critica. Alcuni gestori di interrupt possono effettivamente avere una priorità inferiore rispetto ai thread non interrotti, che è una relazione che non puoi implementare se fai tutto il tuo lavoro all'interno del gestore.
    • Questo diventa ancora più un problema se condividi un'interruzione tra più fonti. Un secondo interrupt potrebbe essere completamente mascherato se non hai ancora terminato l'elaborazione del primo. Alcune architetture hardware sono peggiori rispetto ad altre.
  • Quando le interruzioni provengono da dispositivi esterni, tali dispositivi potrebbero avere aspettative su quanto tempo impieghi a rispondere alle loro interruzioni. Ho avuto ISR complessi che impiegavano troppo tempo per essere eseguiti e il dispositivo esterno ha pensato che fossi morto e ha smesso di parlarmi.
  • Su alcune architetture, gli ISR ​​vengono eseguiti sopra lo stack utilizzato dal programma attualmente in esecuzione (per evitare di allocare memoria per uno stack separato, che potrebbe bloccare, fallire o attivare interruzioni). Non sai quanto spazio di stack avrai a disposizione, quindi non puoi davvero permetterti di fare nient'altro che cancellare lo stato di interruzione e segnalare qualcos'altro per fare il lavoro.
  • Alcune architetture eseguono ISR a un livello di privilegio più elevato rispetto ai thread normali. Potrebbero avere accesso a risorse hardware di basso livello normalmente protette. Ciò implica che ci possono essere implicazioni per la sicurezza nel fare molto lavoro in un ISR.
  • Gli ISR ​​hanno la massima priorità nel tuo sistema. Chiamare una funzione che blocca o si basa internamente sugli interrupt può portare a deadlock.
  • Alcune architetture utilizzano gli interrupt internamente, per cose come l'attivazione dello svuotamento della cache tra i core o la notifica all'ALU che un coprocessore matematico ha terminato di elaborare qualcosa. La manutenzione di queste interruzioni può essere bloccata mentre sei ancora all'interno del tuo ISR, quindi trascorrere troppo tempo qui può avere impatti non evidenti sulle prestazioni.
    • Un altro uso interno degli interrupt potrebbe essere la comunicazione con i motori di debug (JTAG, ecc.). Ho lavorato con chip in cui le sonde di debug non erano molto efficaci mentre si trovavano in un ISR, ma altrimenti funzionavano alla grande. Il debug del codice ISR era un ordine di grandezza più difficile del debug del codice normale.
  • Alcune CPU multi-core serviranno sempre gli interrupt su un core specifico. Fare tutto il tuo lavoro all'interno dell'ISR può forzare l'esecuzione del codice in modo efficace a thread singolo.

Per fortuna, tutti questi problemi hanno la stessa semplice soluzione. Il gestore degli interrupt dovrebbe memorizzare le informazioni necessarie sull'interruzione, cancellare e riattivare l'interruzione, quindi segnalare a qualche altra parte di codice di eseguire il lavoro effettivo. È abbastanza facile da fare sulla maggior parte delle piattaforme che non c'è davvero molto vantaggio nel cercare di stipare tutto il tuo lavoro nello stesso ISR.

"Alcuni gestori di interrupt possono effettivamente avere una priorità inferiore rispetto ai thread non interrotti, che è una relazione che non puoi implementare se fai tutto il tuo lavoro all'interno del gestore."Forse sto fraintendendo questo, ma allora come si attiva questa interruzione?Forse semplicemente non capisco cosa sia un thread.
@DKNguyen- Gli stessi interrupt hanno sempre la priorità sui thread normali, quindi il thread viene messo in attesa e l'ISR viene eseguito.Il lavoro che si desidera eseguire in risposta a tale interruzione non è così importante, quindi è necessario programmarlo per l'esecuzione in un secondo momento e tornare rapidamente all'attività importante che stavi facendo.
Penso che tu abbia frainteso con una domanda formulata male.Immagino che nella mia mente stia pensando a un thread come al ciclo principale, cosa che probabilmente non è, perché se qualcosa avesse una priorità inferiore rispetto al ciclo principale non verrebbe mai eseguito.
Ci sono due nozioni di priorità qui: priorità tecnica (quale thread la CPU sceglie di eseguire) e priorità del mondo reale (lampeggiare un LED non è un grosso problema, ma il ciclo di controllo del reattore nucleare lo è).Quando svolgi il tuo lavoro nell'ISR, puoi entrare in casi in cui queste due nozioni di priorità non sono più sincronizzate e il thread di controllo del reattore è bloccato in attesa che il tuo ISR finisca di far lampeggiare un LED.
oh, capisco cosa intendi: "Alcuni gestori di interrupt possono effettivamente avere un'importanza / urgenza inferiore rispetto ai thread non interrotti, che è una relazione che non puoi implementare se fai tutto il tuo lavoro all'interno del gestore."
* per evitare di allocare memoria per uno stack separato, che potrebbe bloccare, fallire o innescare interruzioni * - Non credo che nessun sistema che esegue interruzioni su uno stack del kernel separato * allochi * un nuovo stack all'inizio di un ISR.Sarebbe lento e folle.Ciò che evita è la necessità che ogni attività nello spazio utente abbia uno "stack del kernel" separato utilizzato per gli interrupt.(O solo uno stack principale vs interrupt in un sistema più semplice, immagino?).O almeno evita di caricare un nuovo puntatore allo stack da qualche parte e * capire * se stavamo già usando lo stack di interrupt.
Attenzione: la mia conoscenza dei thread non è tutto ciò che potrebbe essere, quindi potrei sbagliarmi.Il tuo ciclo principale viene eseguito in un thread.Può generare altri thread, che vengono eseguiti indipendentemente da esso.Pensala in questo modo: il manager (ciclo principale) è il più importante.I suoi subordinati (thread aggiuntivi) svolgono il lavoro che lui / lei dice loro di fare, indipendentemente da ciò che fa.Possono finire il loro lavoro prima del manager, che può controllarli (ma non è tenuto a farlo).Possono anche comunicare al manager cose che cambiano ciò che fa il manager (interrompe), come occuparsi della correzione degli errori o di altre attività.
... Tuttavia, non si desidera che la comunicazione richieda molto tempo perché nega agli altri l'opportunità di comunicare con il manager o, peggio, due subordinati (thread) tentano di comunicare con il manager, provocando comunicazioni incomprensibili che il managernon può adeguatamente capire né rispondere a due.(Pensa di dover avere a che fare con due persone che ti parlano allo stesso tempo e si interrompono a vicenda.)
DKNguyen
2019-11-26 20:39:18 UTC
view on stackexchange narkive permalink

Perché gli interrupt impediscono l'esecuzione di altri interrupt.Tenerli brevi fondamentalmente significa solo non fare più del necessario nell'interrupt.

un grande interrupt con priorità molto bassa e l'altro interrupt

Ciò richiede interrupt annidati (per consentire agli interrupt di interrompersi a vicenda) che possono creare confusione.Non tutti i processi possono essere interrotti in modo sicuro.

Per espandere il punto di DKNguyen: gli interrupt annidati diventano disordinati perché hai bisogno del cosiddetto [re-entrancy] (https://en.wikipedia.org/wiki/Reentrancy_%28computing%29). Per le primitive di sincronizzazione e anche per le primitive di allocazione delle risorse, questa può essere un'aggiunta di grande complessità.Ad esempio, Windows NT 4 (all'epoca) era rientrante mentre io credo che Linux all'epoca non lo fosse.Cercando le parole, vedrai la quantità di letteratura ad essa dedicata.Non è cosa da poco. In ogni caso, quando si scrive un controller da zero, questa complessità aggiuntiva potrebbe non essere desiderabile.
@user247243 Mi fa girare la testa solo al pensiero di provare a scrivere una funzione di rientro, per non parlare di un gestore di interrupt.
Sì, è una vera pita.Vorrei sottolineare per chiarezza che quando dico "NT era completamente rientrante", intendo questo come "il tuo codice doveva essere rientrante e tanto quanto un odore leggermente non rientrante attiverebbe un bugcheck (BSOD) ".È solo un onere generalmente più elevato.
Questo.A volte è necessario trattare con l'hardware _ rapidamente_ altrimenti i dati andranno persi, il che può verificarsi se il gestore non può essere eseguito a causa di un altro gestore di lunga durata.
Jeroen3
2019-11-27 14:26:43 UTC
view on stackexchange narkive permalink

Posso essere una decisione progettuale consapevole per spostare il tuo carico di lavoro solo nelle interruzioni.
Ma è necessario prestare molta attenzione e precauzioni per garantire che le scadenze e lo stack non vengano superati.

Un NVIC è fondamentalmente uno scheduler molto grezzo. Le attività verranno eseguite come priorità impostata, se attivate da un timer, software o altra fonte.
In un ARM Cortex M3 hai un controllo preciso sulle priorità e sull'annidamento. In un ATMega o 8051 no.

Se guardi cosa stai facendo in base ai thread con questo modello, avrai:

  • Thread principale tramite il gestore di ripristino. (inattivo)
    • Interrompi TIMER_ISR_A con un thread normale.
      • Interrompi TIMER_ISR_B con un thread irregolare.
        • Interrompi UART_ISR con un thread di buffer in tempo reale.

Sei già a 3 livelli di profondità, questo significa almeno 3 stackframe!
Con il raggruppamento prioritario puoi limitare l'annidamento a pochi livelli. Ma questo potrebbe costarti delle scadenze.

Con un RTOS preventivo avrai un controllo più preciso, meno interruzioni annidate e meno rischi di far saltare lo stack. E spesso includeva metriche sul rendimento.

E ovviamente tutti gli effetti collaterali dei dati condivisi asincroni e del rientro. Poiché in un ISR non puoi aspettare che qualche risorsa diventi disponibile. Bloccherai tutti i thread con priorità uguale e inferiore dall'esecuzione. A meno che non venga utilizzato solo da thread con priorità più alta.
Molti vincoli impliciti!

Se riesci ad adattare il tuo progetto software per operare entro i vincoli di questo modello, spesso combinato con il modello superloop per attività che non prevedono una scadenza rigida, allora sì, è un progetto valido .
Se adeguatamente documentato per essere compreso anche dal tuo team.

Dmitry Grigoryev
2019-11-27 18:57:14 UTC
view on stackexchange narkive permalink

Per le applicazioni in tempo reale, la tempistica prevedibile è spesso più importante delle prestazioni.Le interruzioni esterne avranno un tempismo imprevedibile, quindi più a lungo dureranno e maggiore sarà l'impatto che avranno sulla tempistica complessiva del sistema.Nota che sto parlando di interrupt ad alta priorità qui: avere un lungo interrupt a bassa priorità non è un problema, fintanto che non influisce sulle cose critiche in termini di tempo (ma attenzione al problema di rientranza menzionato in un'altra risposta).

Ho visto diversi progetti che facevano fatica a ottenere un tempo di I / O coerente nonostante il carico della CPU fosse ragionevolmente basso.Sostituire gli ISR più lunghi eseguendo il polling o riducendoli a semplici gestori di "copia byte / set flag" e facendo il resto in attività periodiche era l'unico modo per tenere sotto controllo quel tempo, a scapito di un po 'di carico aggiuntivo sulla CPU.

Scott Seidman
2019-11-26 20:52:31 UTC
view on stackexchange narkive permalink

Il concetto di interrupt essenzialmente ti consente di fare due cose: le cose normali che faresti normalmente e le cose immediate che devono essere gestite quando si verifica un evento.

Non hai bisogno di fare le cose in questo modo, ma se non lo fai, non stai sfruttando realmente la struttura degli interrupt.Il tuo codice sarà probabilmente più difficile da capire, mantenere ed eseguire il debug.

Qualcosa nelle tue architetture suggerite, però, mi ricorda gli RTOS.

Riguardo a RTOS, è legittimo usare irq per riattivare un thread, ma il thread non è in esecuzione come interrupt, la routine di interrupt è molto breve poiché contrassegna semplicemente il sistema operativo che il thread deve essere riprogrammato poiché l'evento è pronto.
@crasic, esiste un modello per un sistema operativo che cambia attività preventiva in cui i thread vengono effettivamente eseguiti nelle routine del servizio di interruzione.Può essere trovato su https://www.embedded.com/build-a-super-simple-tasker/.Ovviamente richiede interruzioni di annidamento, ma può essere molto utile per piattaforme come l'8051 con spazio di stack limitato.
@semaj Grazie per questo riferimento.Certamente ci sono altre architetture software e requisiti applicativi da considerare.
sktpin
2019-11-28 15:34:54 UTC
view on stackexchange narkive permalink

Non posso ancora commentare, quindi lo posterò qui:

È stato detto da alcuni che si potrebbe progettare con cura un sistema di pianificazione basato sugli interrupt su un hardware che supporti gli interrupt.

Vorrei aggiungere: Sul Cortex-M, ciò sembrerebbe sciocco, tuttavia, poiché ha strutture dedicate per supportare il cambio di contesto per "thread" non basati su interruzioni, in altre parole, invece di dare fastidio alla testa per progettare un sistema di interruzione tutto funzionante, potresti anche usare qualcosa come FreeRTOS?

Cambio di contesto su ARM Cortex-M

Dakkaron
2019-11-29 17:03:30 UTC
view on stackexchange narkive permalink

La grande differenza tra un ciclo principale e gli interrupt è la pianificazione.

Un ciclo principale funziona come un sistema fifo. Passo dopo passo elabora ciò che gli viene richiesto. È lineare, quindi il consumo di memoria è piuttosto deterministico e se chiedi al ciclo principale di fare più di quanto possa gestire, ogni attività verrà comunque eseguita, ma solo più lentamente. È anche piuttosto facile eseguire il debug.

Gli interrupt, quando emessi, interrompono il ciclo principale. Pertanto il ciclo principale viene messo in pausa, il che può causare problemi di temporizzazione se il ciclo principale viene messo in pausa troppo a lungo.

Se viene emesso un secondo interrupt mentre il primo è ancora in esecuzione, ci sono due modi in cui un processore può gestirlo. Il modo in cui è determinato dal processore, ma alcuni processori lasciano scegliere al programmatore. O il processore può rifiutare altri interrupt mentre uno viene gestito, oppure può interrompere l'ultimo interrupt, metterlo nello stack, eseguire il nuovo interrupt e quindi tornare all'ultimo interrupt.

La prima opzione è piuttosto semplice, ma potrebbe far perdere gli interrupt, perché la condizione di interrupt non è più valida quando il primo interrupt termina la sua esecuzione. Inoltre, in questo modo gli interrupt con priorità più bassa possono ostacolare l'esecuzione di interrupt con priorità più alta.

L'altra opzione consente agli interrupt di priorità più alta di interrompere gli interrupt di priorità più bassa e inoltre gli interrupt non possono andare persi. Ma questo è a costo di introdurre problemi relativi alla concorrenza anche su un sistema a thread singolo. Inoltre questo rende il consumo di memoria non deterministico, dal momento che molte esecuzioni di interrupt inserite nello stack aumentano il consumo di memoria di parecchio.

Ho avuto questo problema una volta durante la programmazione di un'applicazione che gestisce i segnali IR su un ATMega328p.La mia routine di interrupt era troppo lunga e lenta e se due mittenti IR avessero inviato un segnale allo stesso tempo all'ATMega (che dovrebbe risultare solo un segnale irriconoscibile) l'esecuzione dell'interruzione del primo fianco del segnale non era terminata prima che arrivasse il segnale successivoin, che ha fatto sì che l'esecuzione dell'interruzione venisse inserita nello stack.Ciò è stato ripetuto fino a quando entrambi i mittenti IR hanno interrotto l'invio.Fino ad allora lo stack aveva centinaia di esecuzioni di interrupt impilate su di esso, il che ha causato un overflow dello stack.Ora, l'ATMega328p non ha la prevenzione dell'overflow dello stack, quindi ha continuato a riversarsi nell'area dell'heap e ha completamente danneggiato l'intera memoria.

Dirk Bruere
2019-11-26 20:53:15 UTC
view on stackexchange narkive permalink

Idealmente, vuoi fare il meno possibile tramite gli interrupt.È molto più facile eseguire il debug di un ciclo che avere interruzioni che saltano apparentemente in modo casuale.Meno sono e meglio è.

A scapito dei sondaggi?
@PeterMortensen Polling è un ciclo.Quanto più possibile dovrebbe essere fatto durante il sondaggio.


Questa domanda e risposta è stata tradotta automaticamente dalla lingua inglese. Il contenuto originale è disponibile su stackexchange, che ringraziamo per la licenza cc by-sa 4.0 con cui è distribuito.
Loading...