Vraag:
Gebruik van globale variabelen in embedded systemen
Rookie91
2014-01-22 09:17:32 UTC
view on stackexchange narkive permalink

Ik ben begonnen met het schrijven van firmware voor mijn product en ik ben hier een groentje. Ik heb veel artikelen doorgenomen over het niet gebruiken van globale variabelen of functies. Is er een limiet voor het gebruik van globale variabelen in een 8 bit systeem of is het een compleet 'Nee-Nee'. Hoe moet ik globale variabelen in mijn systeem gebruiken of moet ik ze volledig vermijden?

Ik zou graag waardevol advies van jullie over dit onderwerp willen inwinnen om mijn firmware compacter te maken.

Deze vraag is niet uniek voor embedded systemen. Een [duplicaat is hier te vinden] (http://stackoverflow.com/questions/484635/are-global-variables-bad).
@Lundin Van uw link: "Tegenwoordig is dat alleen van belang in embedded omgevingen waar het geheugen vrij beperkt is. Iets wat je moet weten voordat je aanneemt dat embedded hetzelfde is als andere omgevingen en aanneemt dat de programmeerregels over de hele linie hetzelfde zijn."
@endolith `static` bestandsbereik is niet hetzelfde als" global ", zie mijn antwoord hieronder.
Zes antwoorden:
richarddonkin
2014-01-22 13:09:57 UTC
view on stackexchange narkive permalink

U kunt met succes globale variabelen gebruiken, zolang u de richtlijnen van @ Phil in gedachten houdt. Hier zijn echter enkele handige manieren om hun problemen te vermijden zonder de gecompileerde code minder compact te maken.

  1. Gebruik lokale statische variabelen voor persistente toestand waartoe u alleen toegang wilt hebben binnen één functie.

      #include <stdint.h>void skipper () {static uint8_t skip_initial_cycles = 5; if (skip_initial_cycles > 0) {skip_initial_cycles - = 1; terugkeren; } / * ... * /}  
  2. Gebruik een structuur om gerelateerde variabelen bij elkaar te houden, om het duidelijker te maken waar ze moeten worden gebruikt en waar niet.

      struct machine_state {uint8_t niveau; uint8_t error_code;} machine_state; struct led_state {uint8_t rood; uint8_t groen; uint8_t blauw;} led_state; void machine_change_state () {machine_state.level + = 1; / * ... * / / * We kunnen gemakkelijk onthouden om led_state niet te gebruiken in deze functie. * /} void machine_set_io () {switch (machine_state.level) {geval 1: PIN_MACHINE_IO_A = 1; / * ... * /}}  
  3. Gebruik globale statische variabelen om de variabelen alleen zichtbaar te maken in het huidige C-bestand. Dit voorkomt onbedoelde toegang door code in andere bestanden als gevolg van naamconflicten.

      / * time_machine.c * / static uint8_t current_time; / * ... * // * delay.c * / static uint8_t current_time; / * Een volledig aparte variabele alleen voor dit C-bestand. * // * ... * /  

Als laatste opmerking, als u een globale variabele wijzigt binnen een interruptroutine en deze ergens anders leest:

  • Markeer de variabele vluchtig.
  • Zorg ervoor dat deze atomisch is voor de CPU (dwz 8-bits voor een 8-bits CPU).

OF

  • Gebruik een vergrendelingsmechanisme om de toegang tot de variabele te beschermen.
vluchtige en / of atomaire variabelen zullen je niet helpen fouten te vermijden, je hebt een soort slot / semafoor nodig, of om onderbrekingen kort te maskeren bij het schrijven naar de variabele.
Vluchtige atomaire variabelen werken perfect in 8-bit CPUS, als ze alleen in één context worden geschreven en alleen in andere contexten worden gelezen. Als u alleen meerdere schrijvers heeft, moet u een vergrendelingsmechanisme gebruiken of interrupts uitschakelen. Vergrendeling is echter meestal vereist, omdat de relevante gegevens meer zijn dan in een atomaire variabele toegang past.
Dat is een nogal enge definitie van "prima werken". Mijn punt was dat iets vluchtigs verklaren, conflicten niet voorkomt. Ook is uw derde voorbeeld geen goed idee - het hebben van twee * afzonderlijke * globalen met dezelfde * naam * maakt de code op zijn minst moeilijker te begrijpen / te onderhouden.
@richarddonkin Heel erg bedankt. Maar ik heb een twijfel over punt 3. Zullen de twee globale statische variabelen als gescheiden worden behandeld als we ze initialiseren in twee aparte bronbestanden in hetzelfde project. Ohk dan moet de compiler bronbestanden als modules behandelen en zulke dingen apart behandelen, toch? Corrigeer mij als ik het mis heb.
@Rookie91 Ja, de compiler behandelt elk C-bronbestand als een compilatie "module", en globale statische variabelen zijn opgenomen in hun respectievelijke "modules". Maar dit geldt alleen voor _static_ globale variabelen. Zoals @John opmerkte, probeer je meestal niet om verschillende globale variabelen met dezelfde naam te noemen; het gebeurt per ongeluk. Als u ze als 'statisch' markeert, voorkomt u dat de compiler ze in de war brengt wanneer u deze fout maakt.
@richarddonkin Heel erg bedankt dat je me het goed hebt laten begrijpen.
Graag gedaan. (Markeer het antwoord als het antwoord als u denkt dat dit het antwoord op uw vraag is.)
@JohnU Je moet vluchtige middelen niet gebruiken om race-omstandigheden te voorkomen, dat zal inderdaad niet helpen. U moet vluchtig gebruiken om gevaarlijke bugs voor compileroptimalisatie te voorkomen die veel voorkomen in compilers van embedded systemen.
@JohnU: Het normale gebruik van `vluchtige` variabelen is om code te laten draaien in de ene uitvoeringscontext om code in een andere uitvoeringscontext te laten weten dat er iets is gebeurd. Op een 8-bits systeem kan een buffer die een macht-van-twee aantal bytes niet groter dan 128 bevat, worden beheerd met één vluchtige byte die het totale levenslange aantal bytes aangeeft dat in de buffer is geplaatst (mod 256) en een andere geeft het levenslange aantal verwijderde bytes aan, op voorwaarde dat slechts één uitvoeringscontext gegevens in de buffer plaatst en slechts één gegevens eruit haalt.
@JohnU: Hoewel het mogelijk is om een ​​of andere vorm van vergrendeling te gebruiken of interrupts tijdelijk uit te schakelen om de buffer te beheren, is het echt niet nodig of nuttig. Als de buffer 128-255 bytes zou moeten bevatten, zou de codering iets moeten veranderen, en als het meer dan dat zou moeten bevatten, zou het uitschakelen van interrupts waarschijnlijk nodig zijn, maar op een 8-bit systeembuffers zijn de buffers meestal klein; systemen met grotere buffers kunnen over het algemeen atomaire schrijfbewerkingen uitvoeren die groter zijn dan 8 bits.
Phil Frost
2014-01-22 09:42:44 UTC
view on stackexchange narkive permalink

De redenen waarom je geen globale variabelen zou willen gebruiken in een 8-bits systeem zijn dezelfde als die je ze niet in een ander systeem zou willen gebruiken: ze maken het moeilijk om te redeneren over het gedrag van het programma.

Alleen slechte programmeurs houden vast aan regels als "gebruik geen globale variabelen". Goede programmeurs begrijpen de reden achter de regels en behandelen de regels meer als richtlijnen.

Is uw programma gemakkelijk te begrijpen? Is zijn gedrag voorspelbaar? Is het gemakkelijk om onderdelen ervan aan te passen zonder andere onderdelen te breken? Als het antwoord op elk van deze vragen ja is, dan ben je op weg naar een goed programma.

Wat @MichaelKaras zei - begrijpen wat deze dingen betekenen en hoe ze de dingen beïnvloeden (en hoe ze je kunnen bijten) is het belangrijkste.
Nick Alexeev
2014-01-22 09:42:07 UTC
view on stackexchange narkive permalink

U moet het gebruik van globale variabelen (afgekort "globals") niet volledig vermijden. Maar u moet ze oordeelkundig gebruiken. De praktische problemen met overmatig gebruik van globals:

  • Globals zijn zichtbaar in de hele compilatie-unit. Elke code in de compilatie-eenheid kan een global. De gevolgen van een wijziging kunnen overal aan de oppervlakte komen waar deze globale evaluatie wordt geëvalueerd.
  • Als resultaat maken globale termen de code moeilijker te lezen en te begrijpen. De programmeur moet altijd alle plaatsen in gedachten houden waar de globale waarde wordt geëvalueerd en toegewezen.
  • Overmatig gebruik van globale waarden maakt de code vatbaarder voor defecten.

Het is een goede gewoonte om een ​​voorvoegsel g_ toe te voegen aan de naam van globale variabelen. Bijvoorbeeld g_iFlags . Als je de variabele met het voorvoegsel in de code ziet, herken je meteen dat het een globaal is.

De vlag * hoeft * niet een globaal te zijn. De ISR zou bijvoorbeeld een functie kunnen aanroepen die een statische variabele heeft.
+1 Ik heb nog nooit van zo'n truc gehoord. Ik heb die alinea uit het antwoord verwijderd. Hoe zou de vlag `static` zichtbaar worden voor de` main () `? Bedoel je dat dezelfde functie die de `statische` heeft, deze later naar de` main () `kan terugbrengen?
Dat is een manier om het te doen. Misschien neemt de functie de nieuwe staat om in te stellen en retourneert de oude staat. Er zijn tal van andere manieren. Misschien heb je een bronbestand met een functie om de vlag in te stellen en een andere om het te krijgen, met een statische globale variabele die de vlagstatus vasthoudt. Hoewel dit technisch gezien een "globale" terminologie is volgens C, is de reikwijdte ervan beperkt tot alleen dat bestand, dat alleen de functies bevat die u moet kennen. Dat wil zeggen, de reikwijdte is niet "globaal", wat echt het probleem is. C ++ biedt aanvullende inkapselingsmechanismen.
Sommige methoden om globals te vermijden (zoals alleen toegang tot hardware via apparaatstuurprogramma's) zijn mogelijk te inefficiënt voor een 8-bits omgeving met veel bronnen.
@SpehroPefhany * Goede programmeurs begrijpen de reden achter de regels en behandelen de regels meer als richtlijnen. * Als de richtlijnen in strijd zijn, weegt de goede programmeur de balans zorgvuldig.
C. Towne Springer
2014-01-22 12:22:51 UTC
view on stackexchange narkive permalink

Het voordeel van globale datastructuren in embedded werk is dat ze statisch zijn. Als elke variabele die je nodig hebt globaal is, zul je nooit per ongeluk te weinig geheugen hebben als functies worden ingevoerd en er ruimte voor wordt gemaakt op de stapel. Maar waarom hebben dan op dat punt functies? Waarom niet één grote functie die alle logica en processen afhandelt - zoals een BASIC-programma zonder dat GOSUB is toegestaan. Als je dit idee ver genoeg doorvoert, heb je een typisch assembleertaalprogramma uit de jaren 70. Efficiënt en onmogelijk te onderhouden en probleemoplossing.

Gebruik globale termen dus oordeelkundig, zoals toestandsvariabelen (bijvoorbeeld als elke functie moet weten of het systeem zich in de status interpreteren of uitvoeren bevindt) en andere gegevensstructuren die door veel functies moeten worden gezien en als @ PhilFrost zegt, is het gedrag van uw functies voorspelbaar? Is er een mogelijkheid om de stapel te vullen met een invoertekenreeks die nooit eindigt? Dit zijn zaken voor het ontwerp van algoritmen.

Merk op dat statisch een verschillende betekenis heeft binnen en buiten een functie. https://stackoverflow.com/questions/5868947/difference-between-static-variable-inside-and-outside-of-a-function

https: //stackoverflow.com/questions/5033627/static-variable-inside-of-a-function-in-c

Veel compilers van embedded systemen wijzen automatische variabelen statisch toe, maar overlay-variabelen die worden gebruikt door functies die niet tegelijkertijd binnen het bereik kunnen zijn; dit levert in het algemeen geheugengebruik op dat gelijk is aan het slechtst mogelijke gebruik voor een gestapeld systeem waarin alle statisch mogelijke oproepsequenties in feite kunnen voorkomen.
supercat
2014-01-23 00:25:48 UTC
view on stackexchange narkive permalink

Globale variabelen mogen alleen worden gebruikt voor echt globale toestanden. Een globale variabele gebruiken om zoiets als bijv. de breedtegraad van de noordgrens van de kaart werkt alleen als er maar één "noordgrens van de kaart" kan zijn. Als de code in de toekomst wellicht moet werken met meerdere kaarten die verschillende noordelijke grenzen hebben, zal code die een globale variabele voor de noordelijke grens gebruikt waarschijnlijk moeten worden herwerkt.

In typische computertoepassingen is er vaak geen een bijzondere reden om aan te nemen dat er nooit meer dan één van iets zal zijn. In embedded systemen zijn dergelijke aannames echter vaak veel redelijker. Hoewel het mogelijk is dat een typisch computerprogramma wordt gebruikt om meerdere gelijktijdige gebruikers te ondersteunen, zal de gebruikersinterface van een typisch ingebed systeem zijn ontworpen voor bediening door een enkele gebruiker die interactie heeft met zijn knoppen en display. Als zodanig zal het op elk moment in de tijd een enkele gebruikersinterface hebben. Het ontwerp van het systeem zodat meerdere gebruikers kunnen communiceren met meerdere toetsenborden en beeldschermen zou veel complexiteit vergen en veel meer tijd in beslag nemen om te implementeren dan het ontwerpen voor één gebruiker. Als er nooit een beroep wordt gedaan op het systeem om meerdere gebruikers te ondersteunen, is elke extra inspanning die wordt geïnvesteerd om dergelijk gebruik mogelijk te maken, verspild. Tenzij het waarschijnlijk is dat ondersteuning voor meerdere gebruikers vereist is, is het waarschijnlijk verstandiger om het risico te lopen dat u de code die wordt gebruikt voor een interface voor één gebruiker moet weggooien in het geval dat ondersteuning voor meerdere gebruikers nodig is, dan extra tijd te besteden aan het toevoegen van meerdere gebruikers. gebruikersondersteuning die waarschijnlijk nooit nodig zal zijn.

Een gerelateerde factor met embedded systemen is dat in veel gevallen (vooral met betrekking tot gebruikersinterfaces) de enige praktische manier om het hebben van meer dan één van iets te ondersteunen, is door meerdere threads te gebruiken. Bij afwezigheid van een andere behoefte aan multi-threading, is het waarschijnlijk beter om een ​​eenvoudig ontwerp met één thread te gebruiken dan de systeemcomplexiteit te vergroten met multi-threading dat waarschijnlijk nooit echt nodig zal zijn. Als het toevoegen van meer dan één van iets sowieso een enorm herontwerp van het systeem zou vereisen, maakt het niet uit of het ook het gebruik van enkele globale variabelen moet herwerken.

Dus het behouden van globale variabelen die niet met elkaar botsen, zal toch geen probleem zijn. bijvoorbeeld: Day_cntr, week_cntr enz. voor het tellen van respectievelijk dagen en week. En ik vertrouw erop dat men niet opzettelijk veel globale variabelen moet gebruiken die op hetzelfde doel lijken en die duidelijk moet definiëren. Heel erg bedankt voor de overweldigende respons. :)
Lundin
2014-01-29 19:08:20 UTC
view on stackexchange narkive permalink

Veel mensen zijn in de war over dit onderwerp. De definitie van een globale variabele is:

Iets dat overal in uw programma toegankelijk is.

Dit is niet hetzelfde als file scope variabelen, die worden gedeclareerd door het trefwoord statisch . Dat zijn geen globale variabelen, het zijn lokale privévariabelen.

  int x; // globale variabele statische int y; // file scope variabele void some_func (void) {...} // toegevoegd om aan te tonen dat de bovenstaande variabelen een file scope hebben.  

Moet je globale variabelen gebruiken? Er zijn een paar gevallen waarin het prima is:

In elke In andere gevallen zult u nooit globale variabelen gebruiken. Er is nooit een reden om dit te doen. Gebruik in plaats daarvan bestandsbereikvariabelen , wat prima is.

Je moet ernaar streven om onafhankelijke, autonome codemodules te schrijven die zijn ontworpen om een ​​specifieke taak uit te voeren. Binnen die modules moeten variabelen voor het interne bestandsbereik zich bevinden als leden van privégegevens. Deze ontwerpmethode staat bekend als object-oriëntatie en wordt algemeen erkend als goed ontwerp.

Waarom de downvote?
Ik * denk * "globale variabele" kan ook worden gebruikt om toewijzingen aan een globaal segment te beschrijven (niet tekst, stapel of heap). In die zin zijn / kunnen statische bestandsvariabelen en statische functievariabelen "globaal" zijn. In de context van deze vraag is het enigszins duidelijk dat globaal verwijst naar scope en niet naar allocatiesegment (hoewel het * mogelijk * is, wist het OP dit niet).
@PaulA.Clayton Ik heb nog nooit gehoord van een formele term genaamd "globaal geheugensegment". Uw variabelen komen terecht op een van de volgende plaatsen: _registers_ of _stack_ (runtime-toewijzing), _heap_ (runtime-toewijzing), _.data_-segment (expliciet geïnitialiseerde statische opslagvariabelen), _.bss_-segment (op nul gestelde statische opslagvariabelen), _. rodata_ (alleen-lezen constanten) of _.text_ (deel van de code). Als ze ergens anders terechtkomen, is dat een projectspecifieke instelling.
@PaulA.Clayton Ik vermoed dat wat u "globaal segment" noemt, het ".data" -segment is.


Deze Q&A is automatisch vertaald vanuit de Engelse taal.De originele inhoud is beschikbaar op stackexchange, waarvoor we bedanken voor de cc by-sa 3.0-licentie waaronder het wordt gedistribueerd.
Loading...