Versenyhelyzet

szoftver programozási fogalom

A versenyhelyzet (angolul race condition) egy hibalehetőség a digitális hálózatokban és a számítógépes programokban. Versenyhelyzet akkor áll fenn, ha a rendszer kimenete függ attól, hogy egymástól független folyamatok milyen sorrendben vagy mekkora időbeli eltéréssel hajtódnak végre. Ha az események nem a tervezett sorrendben hajtódnak végre, akkor hiba keletkezik. A versenyhelyzetet tartalmazó program hibásnak tekintendő.

Versenyhelyzet előfordul az emlősök agyában is. Ezt patkányokon mutatták ki.[1][2]

Az angol elnevezés a Reading Egyetemen, a National Symposium of Logic Design rendezvény alkalmából jelent meg D. Zissostól 1969. március 28-án, a The British Computer Society közreműködésével.

A digitális technikában szerkesztés

 
Versenyhelyzet egy NEM és egy ÉS kapuból álló logikai áramkörben. A bemenő jel (A) megváltozásakor ∆t1 illetve ∆t2 idő kell ahhoz, hogy megváltozzon a NEM, illetve az ÉS kapu kimenő jele. Mivel az ÉS kapunak a NEM kapun át érkező bemenete később változik meg, mint a közvetlen bemenete, a kimenet egy rövid ideig 1 lesz, noha a logikailag helyes kimenet bármilyen bemenet esetén 0.

A digitális hálózatokban a versenyhelyzet a hazárdjelenségek egyike. Az egyes hálózati elemek eltérő jelterjedési ideje miatt a bemenet megváltozása egyes utakon hamarabb okoz változást, mint másokon, ezért a kimeneten rövid ideig hamis (sem a régi, sem az új kimenetnek nem megfelelő) állapot, ún. glitch jelenhet meg. Egyes rendszerek nem érzékenyek az ilyen glitchekre, más rendszereken azonban katasztrofális hatásuk lehet, például egy órajel esetében, aminek az állapotváltásai időzítik a többi áramköri elem működését memóriát használó rendszerek számára. Ebben az esetben a vezérelt rendszer viselkedése hamar megváltozhat, az ideiglenes glitchből tartós glitch lesz.

Olyan hálózatoknál, ahol a kimenet vissza van csatolva a bemenetre (sorrendi hálózat), a glitchek újabb állapotváltozásokat okoznak, és így a rendszer egyre távolabb kerülhet a tervezett működéstől; végül akár egy hibás stabil állapot is kialakulhat (kritikus versenyhelyzet).

A versenyhelyzetek elemzésének és elkerülésének egy lehetséges eszköze a Karnaugh-tábla. Egyes versenyhelyzetek kiküszöbölésére a számításokat redundánsan végzik el. Egy másik eszköz a szinkron sorrendi hálózatok használata; ezekben a kimenetet nem közvetlenül, hanem valamilyen memóriaelemen keresztül csatolják vissza, ami a bemenet megváltozásától egy adott ideig nem veszi figyelembe a kimenet értékét, így az ennél rövidebb ideig tartó glitcheknek nincs hatása.

Egyes logikai elemek metastabil állapotba is kerülhetnek, ami további problémákat okoz.

A versenyhelyzet kritikus, ha a belső változók megváltozásának sorrendje hat a végső állapotra.

Statikus, ha egy jelet komplementerével kombináljuk.

Dinamikus, ha egy elvárt átmenet helyett több állapotátmenet valósul meg. Oka a kapuk közötti interakció. Kiküszöbölhető, ha a rétegek számát kettőben korlátozzuk.

Lényeges, ha egy bemenet hatására két átmenet következik be a visszacsatolásig tartó időn belül. Gyakran késleltetéssel küszöbölik ki.

A számítástechnikában szerkesztés

A versenyhelyzet tipikusan a többszálas programokra és az elosztott rendszerekre jellemző. Az ilyen rendszerekben az egyes szálak vagy alrendszerek egymástól függetlenül, egyidejűleg működnek, ugyanakkor vannak közös erőforrások, amelyekhez mindannyian hozzáférnek. Ha az egyik szálnak a közös erőforrást érintő, egymásra épülő műveletei között egy másik szál is hozzáfér ugyanahhoz az erőforráshoz, az versenyhelyzetet okozhat. Az ilyen rendszerekben kölcsönös kizárással vagy hasonló technikákkal kell garantálni, hogy amíg egy adott szál összetartozó műveleteket végez egy közös erőforráson, addig a többi szál ne férhessen hozzá.

A C11 és C++11 szabványokban definiált memóriamodell a data race kifejezést használja azokra a versenyhelyzetekre, amelyekben megosztott adatokon hajtanak végre potenciálisan konkurrens műveleteket, amelyek közül legalább egy írás. Egy ilyet tartalmazó program viselkedése definiálatlan.[3][4] A Java memóriamodell a Java 5-től kezdve különböző relációkon keresztül értelmezve viselkedést rendel a rosszul szinkronizált programokhoz is.[5]

A versenyhelyzetek nevezetesek arról, hogy nehéz reprodukálni és debuggolni, mivel a program működése nemdeterminisztikus, és az egymással versengő szálak közötti viszonylagos időzítéstől függ. Minden beavatkozás a program működésébe azzal járhat, hogy megváltozik a viszonylagos időzítés, így lehet, hogy a naplózás vagy a debuggoló rendszer teljesen kijavítja a hibát; ezek nélkül a program azonban továbbra is néha hibásan működik. Sőt, a program bármilyen módosítása újra felszínre hozhatja a hibát, ami ismét csak néha fordul elő. A Heisenberg-törvényre utalva ezeket a hibákat heisenbugnak is hívják. Ezért minden olyan program hibásnak tekinthető, ami az ütemezéstől függ.

Példa szerkesztés

A következőben egy egyszerű példát mutatunk be, amiben két szál egy közös változó értékét megnöveli 1-gyel. Az elvárt futás a következő:

Két szál mindegyike megnöveli eggyel egy közös változó értékét.
1. szál 2. szál Közös változó
0
0 0
1 + 0
1 1
1 1 1
1 2 + 1
1 2 2


A fenti esetben a végeredmény 2, ahogy az elvárt. Azonban, ha nincs szinkronizáció, akkor a végeredmény ettől eltérhet. Egy alternatív műveletsorozatot az alábbi táblázat mutat:

A közös változó végső értéke a két szál időzítésétől függ.
1. szál 2. szál Közös változó
0
0 0
0 0 0
1 + 0 0
1 1 + 0
1 1 1
1 1 1


Ekkor a végeredmény 1 az elvárt 2 helyett. Ennek oka, hogy a hozzáférés kölcsönös kizárás nélkül történik. A kölcsönös kizárás azt jelenti, hogy egy másik szál nem férhet hozzá ahhoz az adathoz, amivel egy másik éppen műveleteket végez.

Biztonság szerkesztés

A versenyhelyzetnek következményei lehetnek biztonsági szempontból is. Ez lehetővé teszi, hogy a támadó hozzáférjen a megosztott erőforráshoz, így a többi aktor hibásan kezd viselkedni, ami okozhat szolgáltatásmegtagadást[6] és a jogkör kiterjesztését eredményezheti.[7][8]

Speciális helyzet adódik, ha egy predikátumot kell ellenőrizni, például azonosítás szempontjából. A predikátumon végzett műveletek hatására értéke megváltozhat az ellenőrzés és a használat között. Biztonságkritikus kódban az efféle hibát time-of-check-to-time-of-use (TOCTTOU) bugnak nevezik.

Versenyhelyzeteket szándékosan használnak hardveres véletlengenerátorokhoz és fizikailag klónozhatatlan függvényekhez (PUF). Ez utóbbiakhoz az áramkört úgy tervezik, hogy egy csúcs több úton is elérhető legyen, és véletlenszerűen lesz az egyik vagy másik gyorsabb.

Fájlrendszerek szerkesztés

Ha két vagy több program egyszerre fér hozzá egy fájlhoz, akkor az adatvesztéshez vagy a jogkör kiterjesztéséhez vezethet.[7] A leggyakrabban alkalmazott módszer a fájlok zárolása. Egy másik lehetőség, hogy egy démonnak kizárólagos hozzáférése van a fájlhoz, és amelyik program hozzá akar férni, azt csak a démonnal kommunikálva teheti. Ez folyamatszintű szinkronizációt igényel.

Előfordulhat, hogy ugyan a programok nem férnek hozzá ugyanahhoz a fájlhoz, viszont versengenek egymással más erőforrásokért, tárhelyért, memóriáért, processzoridőért. A nem megfelelően tervezett programok viselkedése megjósolhatatlanná válhat. Egy nagyon megbízható rendszerben egy ilyen hiba sokáig elkerülheti a figyelmet, azonban a tárhely fogytával vagy túl sok program indításával a rendszer több része elveszti stabilitását. Erre példa, hogy majdnem elvesztették a kapcsolatot a Spirit marsjáróval nem sokkal a leszállás után. Egy megoldás, hogy a programnak először is le kell foglalnia a szükséges erőforrásokat, mielőtt végrehajtja feladatát. Ha ez nem sikerül, akkor addig vár, amíg az erőforrások fel nem szabadulnak. A program végezhet hibakezelést is, felkészülve ezzel ezekre a hibákra. További lehetőség, hogy minden feladat befejezésekor ellenőrzést végez, és ha nem sikerül, akkor visszagöngyölíti. Gyakoribb, hogy előzőleg ellenőrzi, hogy van elég erőforrás, de ez nem teljesen biztos, mivel megjósolhatatlan, hogy más programok mit fognak csinálni.

Hálózatok szerkesztés

Egy IRC-hez hasonló elosztott chat hálózatban az a felhasználó, amelyik megnyit egy csatornát, adminisztrátori jogot szerez arra a csatornára. Versenyhelyzet fordul elő, ha két felhasználó két szerverről ugyanazzal a névvel nyit csatornát ugyanoda, akkor mindketten megkapják az adminisztrátori jogot, mivel egyik szerver sem értesült arról, hogy ez a csatorna már meg van nyitva. Ezt a problémát azóta már megoldották az implementációk.

Ebben az esetben a megosztott erőforrásba beletartozik a hálózat állapota (mely csatornák léteznek, kik nyitották őket, ebből következően kiknek vannak előjogai). Ezt bármely szerver megváltoztathatja, de értesítenie kell a többi szervert a változásról. Azonban ez az információ lehet, hogy késve érkezik meg. Egy külön névszerver beiktatása, ami adminisztrálná a változásokat, központosítaná a hálózatot.

Versenyhelyzet nem blokkoló socketekkel is előfordulhat. Ekkor a program sebessége függhet a hálózati kapcsolat sebességétől.

Biztonságkritikus rendszerek szerkesztés

Biztonságkritikus rendszerekben a versenyhelyzet különösen nagy veszéllyel jár, hiszen nagy anyagi kárt, akár halált is okozhat. A Therac-25 sugárkezelő gép nyalábjait irányító rendszerek közötti versenyhelyzet legalább három beteg halálát és több beteg sérülését okozták.[9]

Egy másik példa a GE Energy által alkotott Energy Management System. Ezt használta az Ohio központú FirstEnergy Corp is. A versenyhelyzet a figyelmeztető alrendszerben adódott. A rendszer nem figyelmeztetett, ha egyszerre legalább három távvezetékben adódott hiba, így azokat később vették észre. 2003 augusztus 14-én áramszünet alakult ki az Amerikai Egyesült Államok északkeleti részén és Ontario kanadai tartományban.[10] Ezt a hibát a GE Energy javította.

Eszközök szerkesztés

A versenyhelyzet felismerését és kezelését több eszköz segíti. Ezek lehetnek statikus vagy dinamikus elemzők.

A Thread Safety Analysis statikus elemző annotációkon alapuló procedúrán belüli elemzést végez. Eredetileg a gcc egy ága tartalmazta, de a Clangben is megvalósították, ahol is támogatja a PThreads szálakat.[11]

A dinamikus eszközök közé tartozik az Intel Inspector, ami ellenőrzi a szálakat, a memóriát, hogy növelje a megbízhatóságot, biztonságot és pontosságot Fortran, C és C++ alkalmazásokban. Az Intel Advisor mintavételezésen alapul, SIMD vektorizációs optimalizációt végez, továbbá szálkezelést és memóriahasználatot elemez C, C++, C# és Fortran programok számára. A ThreadSanitizer vizsgálja a forrást és a már lefordított bináris programot.[12] LLVM alapú, támogatja a PThreads szálakat. A Helgrind egy Valgrind alapú eszköz, ami felderíti a szinkronizációs hibákat C, C++ és Fortran programokban, amelyek POSIX pthreads szálakkal működnek.[13]

Jegyzetek szerkesztés

  1. How Brains Race to Cancel Errant Movements. Discover Magazine blogs, 2013. augusztus 3. [2013. augusztus 6-i dátummal az eredetiből archiválva]. (Hozzáférés: 2018. május 17.)
  2. (2013) „Canceling actions involves a race between basal ganglia pathways”. Nature Neuroscience 16 (8), 1118–24. o. DOI:10.1038/nn.3456. PMID 23852117.  
  3. ISO/IEC 9899:2011 - Information technology - Programming languages - C. Iso.org. (Hozzáférés: 2018. január 30.)
  4. ISO/IEC 14882:2011. ISO, 2011. szeptember 2. (Hozzáférés: 2011. szeptember 3.)
  5. http://kto.web.elte.hu/hu/oktatas/eak2/concurrency.pdf
  6. CVE-2015-8461: A race condition when handling socket errors can lead to an assertion failure in resolver.c. Internet Systems Consortium. [2016. június 9-i dátummal az eredetiből archiválva]. (Hozzáférés: 2017. június 5.)
  7. a b Vulnerability in rmtree() and remove_tree(): CVE-2017-6512. CPAN. (Hozzáférés: 2017. június 5.)
  8. security: stat cache *very large* race condition if caching when follow_symlink disabled. lighttpd. (Hozzáférés: 2017. június 5.)
  9. An Investigation of Therac-25 Accidents — I. Courses.cs.vt.edu. (Hozzáférés: 2011. szeptember 19.)
  10. Kevin Poulsen: Tracking the blackout bug. Securityfocus.com, 2004. április 7. (Hozzáférés: 2011. szeptember 19.)
  11. Thread Safety Analysis
  12. THREADSANITIZER
  13. Helgrind: a thread error detector

Források szerkesztés

Fordítás szerkesztés

  • Ez a szócikk részben vagy egészben a Race condition című angol Wikipédia-szócikk fordításán alapul. Az eredeti cikk szerkesztőit annak laptörténete sorolja fel. Ez a jelzés csupán a megfogalmazás eredetét és a szerzői jogokat jelzi, nem szolgál a cikkben szereplő információk forrásmegjelöléseként.