Mutex

Az oldal jelenlegi verzióját még nem ellenőrizték tapasztalt közreműködők, és jelentősen eltérhet a 2021. augusztus 31-én felülvizsgált verziótól ; az ellenőrzések 5 szerkesztést igényelnek .

A Mutex ( angol  mutex , kölcsönös kizárásból  - „kölcsönös kizárás”) egy szinkronizálási primitív, amely biztosítja a kód kritikus szakaszainak végrehajtásának kölcsönös kizárását [1] . A klasszikus mutex abban különbözik a bináris szemafortól , hogy kizárólagos tulajdonosa van, akinek el kell engednie (vagyis zárolatlan állapotba kell vinnie) [2] . A mutex abban különbözik a spinlocktól , hogy a vezérlést az ütemezőnek adja át a szálváltáshoz, amikor a mutex nem szerezhető [3] . Léteznek megosztott mutexnek nevezett olvasási-írási zárak is, amelyek az exkluzív záron kívül olyan megosztott zárat is biztosítanak, amely lehetővé teszi a mutex megosztott tulajdonjogát, ha nincs kizárólagos tulajdonos [4] .

Hagyományosan egy klasszikus mutex változóként ábrázolható, amely két állapotú lehet: zárt és feloldott. Amikor egy szál belép a kritikus szakaszába, meghív egy függvényt a mutex zárolására, és blokkolja a szálat, amíg a mutex fel nem szabadul, ha már egy másik szál birtokolja. A kritikus szakaszból való kilépéskor a szál meghívja a függvényt, hogy a mutexet zárolatlan állapotba helyezze. Ha a feloldás során egy mutex több szálat blokkol, az ütemező kiválaszt egy szálat a végrehajtás folytatásához (az implementációtól függően ez lehet véletlenszerű szál vagy valamilyen kritérium által meghatározott szál) [5] .

A mutex feladata, hogy megvédje az objektumot attól, hogy más szálak hozzáférjenek, mint ami a mutex tulajdonosa. Egy adott pillanatban csak egy szál birtokolhat egy mutex által védett objektumot. Ha egy másik szálnak hozzá kell férnie a mutex által védett adatokhoz, akkor az a szál blokkolva lesz, amíg a mutex fel nem szabadul. A mutex megvédi az adatokat az aszinkron változások miatti sérüléstől ( versenyhelyzet ), de helytelen használat esetén más problémák is előfordulhatnak, például holtpont vagy kettős rögzítés .

A megvalósítás típusa szerint a mutex lehet gyors, rekurzívvagy hibakezeléssel.

Használati problémák

Prioritás inverzió

A prioritás inverziója akkor történik, amikor egy magas prioritású folyamatnak kell végrehajtania, de az alacsony prioritású folyamat tulajdonában lévő mutexre zárol, és meg kell várnia, amíg az alacsony prioritású folyamat feloldja a mutexet. A korlátlan prioritás-inverzió klasszikus példája a valós idejű rendszerekben, amikor egy közepes prioritású folyamat lefoglalja a CPU-időt, aminek következtében az alacsony prioritású folyamat nem tud futni, és nem tudja feloldani a mutexet [6] .

A probléma tipikus megoldása a prioritás öröklődés, amelyben egy mutex-et birtokló folyamat egy másik általa blokkolt folyamat prioritását örökli, ha a blokkolt folyamat prioritása magasabb, mint az aktuálisé [6] .

Alkalmazás programozás

Mutexek a Win32 API-ban

A Windows Win32 API-ja két mutexe-megvalósítással rendelkezik - maguk a mutexek, amelyeknek neve van, és különböző folyamatok között használhatók [7] , valamint kritikus szakaszok , amelyeket csak ugyanazon a folyamaton belül használhatnak különböző szálak [8] . A mutexek mindkét típusának megvan a maga rögzítési és feloldási funkciója [9] . A Windows kritikus szakasza valamivel gyorsabb és hatékonyabb, mint a mutex és a szemafor, mivel a processzor-specifikus teszt és beállítás utasítást használja [8] .

Mutexek a POSIX-ben

A Pthreads csomag különféle funkciókat biztosít a szálak szinkronizálására [10] . Ezen funkciók között vannak mutexekkel való munkavégzésre szolgáló funkciók. A mutex beolvasási és feloldási függvények mellett rendelkezésre áll egy mutex adatszerzési kísérlet funkció, amely hibát ad vissza, ha szálblokkolás várható. Ez a funkció aktív várakozási ciklusban használható, ha szükséges [11] .

Pthreads csomagfüggvények mutexekkel való munkához
Funkció Leírás
pthread_mutex_init() Mutex létrehozása [11] .
pthread_mutex_destroy() Mutex pusztítás [11] .
pthread_mutex_lock() Mutex átvitele zárolt állapotba (mutex rögzítés) [11] .
pthread_mutex_trylock() Próbálja meg a mutexet blokkolt állapotba helyezni, és hibaüzenetet ad vissza, ha a szálnak blokkolnia kell, mert a mutexnek már van tulajdonosa [11] .
pthread_mutex_timedlock() Próbálja meg a mutexet zárolt állapotba helyezni, és hibaüzenetet ad vissza, ha a kísérlet a megadott idő előtt meghiúsult [12] .
pthread_mutex_unlock() A mutex átvitele zárolatlan állapotba (a mutex feloldása) [11] .

Speciális problémák megoldására a mutexekhez különféle attribútumok rendelhetők [11] . Az attribútumokon keresztül a függvény pthread_mutexattr_settype()segítségével beállíthatja a mutex típusát, amely befolyásolja a mutex rögzítésére és felszabadítására szolgáló függvények viselkedését [13] . A mutex három típus egyike lehet [13] :

Mutexek C-ben

A C programozási nyelv C17 szabványa definiál egy típust mtx_t[15] és a vele együtt használható függvénykészletet [16] , amelynek elérhetőnek kell lennie, ha a makrót __STDC_NO_THREADS__nem definiálta a fordító [15] . A mutexek szemantikája és tulajdonságai általában összhangban vannak a POSIX szabvánnyal.

A mutex típusát úgy határozzuk meg, hogy egy jelzőkombinációt adunk át a mtx_init()[17] függvénynek :

A C17 szabvány nem veszi figyelembe a mutexek osztott memórián keresztüli használatának lehetőségét a különböző folyamatokban.

Mutexek a C++-ban

A C++ programozási nyelv C++17 szabványa 6 különböző mutex osztályt határoz meg [20] :

A Boost könyvtár ezenkívül névvel ellátott és folyamatközi mutexet, valamint megosztott mutexet is biztosít, amelyek lehetővé teszik, hogy több, csak olvasható adatszálon keresztül megosztott tulajdonjogot biztosító mutexet szerezzenek írási kizárás nélkül a zárolás beszerzésének idejére. lényegében egy olvasási-írási zárolási mechanizmus [25] .

A megvalósítás részletei

Az operációs rendszerek szintjén

A mutex általában nem csak az állapotát tárolja, hanem a blokkolt feladatok listáját is. A mutex állapotának megváltoztatása architektúrától függő atomműveletekkel valósítható meg felhasználói kód szinten, de a mutex feloldásakor a mutex által blokkolt egyéb feladatokat is folytatni kell. Ezekre a célokra alkalmas egy alacsonyabb szintű szinkronizálási primitív - a futex , amely az operációs rendszer oldalán van implementálva, és átveszi a blokkoló és feloldó feladatokat, lehetővé téve többek között folyamatközi mutexek létrehozását [26] . A futex használatával a mutex a Pthreads csomagban valósul meg számos Linux disztribúcióban [27] .

x86 és x86_64 architektúrákon

A mutexek egyszerűsége lehetővé teszi, hogy a felhasználói térben olyan assembler utasítással valósíthatók meg, XCHGamely képes a mutex értékét atomosan egy regiszterbe másolni, és ezzel egyidejűleg a mutex értékét 1-re állítani (korábban ugyanabba a regiszterbe írták). A nulla mutex érték azt jelenti, hogy zárolt állapotban van, míg az egyes érték azt, hogy zárolatlan állapotban van. A regiszterből származó érték 0-ra tesztelhető, és nulla érték esetén a vezérlést vissza kell adni a programnak, ami azt jelenti, hogy a mutex megtörténik, ha az érték nem nulla volt, akkor a vezérlést át kell adni a programnak. az ütemező egy másik szál munkájának folytatására, majd egy második kísérlet a mutex megszerzésére, amely az aktív blokkolás analógjaként szolgál. A mutex feloldása a 0 érték eltárolásával történik a mutexben a XCHG[28] paranccsal . Alternatív megoldásként LOCK BTS(TSL implementáció egy bitre) vagy CMPXCHG[29] ( CAS implementáció ) használható.

A vezérlés átadása az ütemezőnek elég gyors ahhoz, hogy ne legyen tényleges aktív várakozási hurok, mivel a CPU egy másik szál végrehajtásával lesz elfoglalva, és nem lesz tétlen. A felhasználói térben végzett munka lehetővé teszi a processzoridő szempontjából költséges rendszerhívások elkerülését [30] .

Az ARM architektúrában

Az ARMv7 architektúra úgynevezett lokális és globális exkluzív monitorokat használ a memória szinkronizálására a processzorok között, amelyek állapotgépek, amelyek vezérlik a memóriacellákhoz való atomi hozzáférést [31] [32] . LDREXA [33] utasítás segítségével egy memóriacella atomi olvasása, az utasításon keresztül pedig egy atomi írás végezhető STREX, amely egyben a művelet sikerjelzőjét is visszaadja [34] .

A mutex rögzítési algoritmus magában foglalja az érték beolvasását LDREXés az olvasási érték ellenőrzését egy zárolt állapothoz, amely megfelel a mutex változó 1-es értékének. Ha a mutex zárolva van, a zár feloldására váró kód meghívódik. Ha a mutex feloldott állapotban volt, akkor a zárolást az íráskizáró utasítással lehet megkísérelni STREXNE. Ha az írás azért nem sikerül, mert a mutex értéke megváltozott, akkor a rögzítési algoritmus elölről megismétlődik [35] . A mutex rögzítése után az utasítás végrehajtásra kerül DMB, amely garantálja a mutex által védett erőforrás memóriájának integritását [36] .

A mutex feloldása előtt az utasítást is meghívják DMB, ami után a mutex változóba a 0 értéket írjuk az utasítás segítségével STR, ami a zárolatlan állapotba átvitelt jelenti. A mutex feloldása után a várakozó feladatokat, ha vannak, jelezni kell, hogy a mutex feloldásra került [35] .

Lásd még

Jegyzetek

  1. Tanenbaum, 2011 , 2.3.6. Mutexes, p. 165.
  2. Oleg Tsiljurik. Kernel programozási eszközök: 73. rész. Párhuzamosság és szinkronizálás. Zárak. 1. rész . - www.ibm.com, 2013. - augusztus 13. — Hozzáférés időpontja: 2019.12.06.
  3. Az Open Group Base Specifications 7. kiadás, 2018-as kiadás, Rendszerinterfészek magyarázata, Általános  információk . Letöltve: 2020. június 20. Az eredetiből archiválva : 2020. június 18.
  4. 1 2 3 C++17, 2017 , 33.4.3.4 Megosztott mutex típusok, p. 1373.
  5. Tanenbaum, 2011 , 2.3.6. Mutexes, p. 165-166.
  6. ↑ 1 2 Steven Rostedt, Alex Shi. RT-mutex megvalósítási terv – A Linux Kernel dokumentációja  . A Linux Kernel dokumentációja . A kernelfejlesztő közösség (2017. június 7.). Letöltve: 2020. június 16. Az eredetiből archiválva : 2020. június 16.
  7. Mutex létrehozása . Hozzáférés dátuma: 2010. december 20. Az eredetiből archiválva : 2012. február 14.
  8. ↑ 1 2 Michael Satran, Drew Batchelor. Kritikus  szakaszobjektumok . Dokumentáció . Microsoft (2018. május 31.). Hozzáférés dátuma: 2010. december 20. Az eredetiből archiválva : 2012. február 14.
  9. Michael Satran, Drew batchelor. Szinkronizálási funkciók – Win32 alkalmazások  . Dokumentáció . Microsoft (2018. május 31.). Letöltve: 2020. június 18. Az eredetiből archiválva : 2020. június 18.
  10. Tanenbaum, 2011 , 2.3.6. Mutexes, Mutexes a Pthreads, p. 167.
  11. 1 2 3 4 5 6 7 Tanenbaum, 2011 , 2.3.6. Mutexes, Mutexes a Pthreads, p. 168.
  12. IEEE, The Open Group. pthread_mutex_timedlock  (angol) . pubs.opengroup.org . The Open Group (2018). Letöltve: 2020. június 18. Az eredetiből archiválva : 2020. június 18.
  13. ↑ 1 2 IEEE, The Open Group. pthread_mutexattr_settype(3  ) . Az Open Group Base Specifications 7. szám, 2018-as kiadás . The Open Group (2018). Hozzáférés dátuma: 2010. december 20. Az eredetiből archiválva : 2012. február 14.
  14. ↑ 1 2 3 IEEE, The Open Group. pthread_mutex_lock  (angol) . Az Open Group Base Specifications 7. szám, 2018-as kiadás . The Open Group (2018). Letöltve: 2020. június 17. Az eredetiből archiválva : 2019. szeptember 17.
  15. 1 2 C17, 2017 , 7.26 Szálak <szálak.h>, p. 274.
  16. C17, 2017 , 7.26.4 Mutex függvények, p. 277-279.
  17. C17, 2017 , 7.26.4.2 Az mtx_init függvény, p. 277-278.
  18. C17, 2017 , 7.26.1. Bevezetés, 1. o. 274.
  19. 1 2 C17, 2017 , 7.26.1 Bevezetés, p. 275.
  20. C++17, 2017 , 33.4.3.2 Mutex típusok, p. 1368.
  21. C++17, 2017 , 33.4.3.2.1. Osztálymutex, p. 1369-1370.
  22. C++17, 2017 , 33.4.3.2.2 Osztály rekurzív_mutex, p. 1370.
  23. C++17, 2017 , 33.4.3.3 Időzített mutex típusok, p. 1370-1371.
  24. C++17, 2017 , 33.4.3.3.2 Osztály rekurzív_időzített_mutex, p. 1372-1373.
  25. Szinkronizálási  mechanizmusok . Boost C++ Libraries 1.73.0 . Letöltve: 2020. június 18. Az eredetiből archiválva : 2020. június 18.
  26. Ulrich Drapper. A futexek trükkösek  : [ arch. 2020.06.24 .] : [PDF]. - Red Hat, Inc., 2005. - december 11.
  27. Karim Yaghmour, Jon Masters, Gilad Ben-Yossef, Philippe Gerum. Beágyazott Linux rendszerek építése: fogalmak, technikák, trükkök és csapdák . - "O'Reilly Media, Inc.", 2008. - S. 400. - 466 p. - ISBN 978-0-596-55505-4 .
  28. Tanenbaum, 2011 , 2.3.6. Mutexes, p. 166.
  29. Steven Rostedt. RT-mutex megvalósítás  tervezése . A Linux Kernel dokumentációja (2017. június 7.). Letöltve: 2021. augusztus 30. Az eredetiből archiválva : 2021. augusztus 13.
  30. Tanenbaum, 2011 , 2.3.6. Mutexes, p. 166-167.
  31. ARM, 2009 , 1.2.1 LDREX és STREX, p. négy.
  32. ARM, 2009 , 1.2.2 Exkluzív monitorok, p. 5.
  33. ARM, 2009 , 1.2.1 LDREX és STREX, LDREX, p. négy.
  34. ARM, 2009 , 1.2.1 LDREX és STREX, STREX, p. négy.
  35. 1 2 ARM, 2009 , 1.3.2 Mutex megvalósítása, p. 12-13.
  36. ARM, 2009 , 1.2.3 Memóriakorlátok, p. nyolc.

Irodalom