Atomos hozzáférés a változókhoz

A beágyazott alkalmazások hibakeresése során nehezebb olyan hibákat felvenni, amelyek nem mindig állandóak, hanem csak időről időre. Az ilyen hibák egyik oka: olyan változók, amelyek aszinkron módon érkeznek. Az ilyen változókat megfelelően meg kell határozni, és megfelelő védelmet kell biztosítani.

A meghatározásnak tartalmaznia kell a kulcsszó változékony. Értesíti a fordítót, hogy egy változó nem csak a jelenlegi végrehajtható kódról, hanem más helyről is módosítható. Ezután a fordító elkerüli a változók bizonyos optimalizálását.
Egy közös változó védelme érdekében minden egyes hozzáférési műveletnek atomnak kell lennie. Vagyis nem szabad félbeszakítani a végéig. Például egy 32 bites vagy 16 bites változó elérése egy 8 bites architektúrán nem atomikus, mivel az olvasási vagy írási műveletekhez több utasítás szükséges.
Tekintsünk egy tipikus példát egy nyilvános változóra - egy programidőzítőre. A megszakításkezelőben az értéke megváltozik, és a fő kódban olvasható. Ha más megszakítások le vannak tiltva a kezelőben, például akkor, ha az AVR mikrokontrollerekben az alapértelmezett beállítás történik, akkor a változó változtatásának működése atomos, és nem lép fel.

illékony, unsigned long system_timer = 0;

#pragma vector = TIMER0_COMP_vect
__interruptvoid Timer0CompVect (void)
system_timer ++;
>

Másrészt a fő hurokban a megszakítási programok leggyakrabban megoldódnak, és a nem biztonságos kód változata így néz ki:

ha (system_timer> = next_cycle)
next_cycle + = 100;
do_ valami ();
>

Ez a kód nem biztonságos, mert a változó rendszer_timer olvasásának működése nem atomi. A változó rendszer_timer egyik byte-jának olvasása közben megszakadhat TIMER0_COMP, és a kezelő megváltoztatja az értékét. Ezután a fő programra való visszatérés után a változó többi részét már az új értékről olvassuk le. Bizonyos esetekben a régi és az új értékek keveréke nem okoz hibákat, de másokban nagymértékben befolyásolja a program viselkedését. Nos, például, ha a system_timer régi értéke 0x00ffffff, az új pedig 0x01000000.

A változó rendszer_timerhez való hozzáférés védelme érdekében használhatja a monitorfunkciót, ehhez a _monitor kulcsszó meg van adva a függvény neve előtt.


ha (get_system_timer ()> = next_cycle)
next_cycle + = 100;
do_ valami ();
>

A monitor funkció egy olyan funkció, amely a bemeneten tárolja a SREG regisztert, letiltja a megszakításokat a végrehajtás időtartamára, és visszaállítja a SREG tartalmát a kimenet előtt.

Ha azt szeretné, hogy a megszakítások letilthatók legyenek a kód egy adott területén, akkor belső funkciókat használhat.

...
aláíratlan hosszú tmp;
unsigned char oldState;
oldState = __save_interrupt (); // mentse a SREG regisztert
__disable_interrupt (); // megtiltsák a megszakításokat
tmp = rendszer_timer; // olvasd el a system_timer értékét az ideiglenes változón __restore_interrupt (oldState); // helyreállítás SREG

A C ++ eszközök segítségével integrálhatja ezt a logikát az osztályba.

Mutex ()
__restore_interrupt (current_state);
>

aláíratlan hosszú tmp;
Mutex m; // hozzon létre egy osztály objektumot, mostantól a hozzáférés atomos lesz
tmp = rendszer_timer; // mentse el a system_timer egy ideiglenes változást
>

Az m objektum létrehozásakor a konstruktor tárolja a SREG regisztert, és letiltja a megszakításokat. A blokk végén a megsemmisítő visszaállítja a SREG tartalmát. Gyönyörű, mi?

Általánosságban elmondható, hogy az elv mindenütt létezik, de számos megvalósítási lehetőség létezik. Például egy változó elérésekor nem tilthat meg minden megszakítást, csak azok, akik ezt a változót használják.

Ez minden. Minden jó hibakeresés.

A probléma egy 8 bites változóval lehetséges. Egy olyan műveletet, mint a system_timer - = 100 összeállítódik több szerelői utasításba, és a fő kód is megszakítható a rendszer_timer olvasása és az eredmény írása között.
---------
Van egy másik módja a multibyte aszinkron számlálók olvasásának (a megszakítások megszakítása nélkül) - kétszer meg kell számolni a változót, és összehasonlítani kell a bájtokat, kivéve a fiatalabbat. Ha a példányok bájtja egyenlő - adja meg az utolsó olvasott értéket, ha nem egyenlő -, amíg a bájtok utolsó két olvasási értéke egyenlő. A számlálók alacsony bájtja megváltozhat az átvitel nélkül, ezért nem vesz részt az ellenőrzésben.

Amint a példákból látható, a kód az IAR fordítóhoz tartozik. A WinAVR-ben egy hasonló probléma megoldható egy fájl beillesztésével. amelyben makrók definiáltak az atomi hozzáférés megvalósításához.
például:
.
ATOMIC_BLOCK (ATOMIC_RESTORESTATE)
// kódblokk tiltott megszakításokkal
>
.

Kapcsolódó cikkek