Mutex

Den aktuella versionen av sidan har ännu inte granskats av erfarna bidragsgivare och kan skilja sig väsentligt från versionen som granskades den 31 augusti 2021; kontroller kräver 5 redigeringar .

Mutex ( engelska  mutex , från ömsesidig uteslutning  - "ömsesidig uteslutning") är en synkroniseringsprimitiv som ger ömsesidig uteslutning av exekvering av kritiska avsnitt av kod [1] . En klassisk mutex skiljer sig från en binär semafor genom närvaron av en exklusiv ägare, som måste släppa den (dvs överföra den till ett olåst tillstånd) [2] . En mutex skiljer sig från en spinlock genom att skicka kontrollen till schemaläggaren för att byta trådar när mutex inte kan erhållas [3] . Det finns även läs- och skrivlås som kallas delade mutex som tillhandahåller, förutom det exklusiva låset, ett delat lås som tillåter delat ägande av mutexen om det inte finns någon exklusiv ägare [4] .

Konventionellt kan en klassisk mutex representeras som en variabel som kan vara i två tillstånd: låst och olåst. När en tråd går in i dess kritiska sektion anropar den en funktion för att låsa mutex, blockerar tråden tills mutex släpps om en annan tråd redan äger den. När du lämnar den kritiska sektionen anropar tråden funktionen för att flytta mutexen till det olåsta tillståndet. Om det finns flera trådar blockerade av en mutex under upplåsning, väljer schemaläggaren en tråd för att återuppta exekveringen (beroende på implementeringen kan detta antingen vara en slumpmässig tråd eller en tråd som bestäms av vissa kriterier) [5] .

En mutexs uppgift är att skydda objektet från att nås av andra trådar än den som äger mutexen. Vid varje givet ögonblick kan bara en tråd äga ett objekt skyddat av en mutex. Om en annan tråd behöver åtkomst till data som skyddas av mutex, kommer den tråden att blockeras tills mutex släpps. En mutex skyddar data från att skadas av asynkrona förändringar ( ett rastillstånd ), men andra problem som dödläge eller dubbelfångst kan orsakas om de används på fel sätt .

Efter typ av implementering kan mutex vara snabb, rekursiveller med felkontroll.

Användningsproblem

Prioritetsinversion

En prioritetsinvertering inträffar när en process med hög prioritet ska köras, men den låser på en mutex som ägs av den lågprioriterade processen och måste vänta tills den lågprioriterade processen låser upp mutexen. Ett klassiskt exempel på obegränsad prioritetsinversion i realtidssystem är när en process med medelprioritet tar CPU-tid, vilket gör att processen med låg prioritet inte kan köras och inte kan låsa upp mutexen [6] .

En typisk lösning på problemet är prioritetsarv, där en process som äger en mutex ärver prioriteten för en annan process som blockeras av den, om prioriteten för den blockerade processen är högre än den för den nuvarande [6] .

Applikationsprogrammering

Mutexes i Win32 API

Win32 API i Windows har två implementeringar av mutexes - mutexes själva, som har namn och är tillgängliga för användning mellan olika processer [7] , och kritiska sektioner , som endast kan användas inom samma process av olika trådar [8] . Var och en av dessa två typer av mutexer har sina egna fångst- och släppfunktioner [9] . Den kritiska delen av Windows är något snabbare och mer effektiv än mutex och semafor eftersom den använder den processorspecifika test-and-set- instruktionen [8] .

Mutexes i POSIX

Pthreads - paketet tillhandahåller olika funktioner som kan användas för att synkronisera trådar [10] . Bland dessa funktioner finns funktioner för att arbeta med mutexes. Förutom mutex-förvärvnings- och frigöringsfunktionerna tillhandahålls en mutex-förvärvningsförsöksfunktion som returnerar ett fel om en trådblockering förväntas. Denna funktion kan användas i en aktiv vänteloop om behov uppstår [11] .

Pthreads-paketfunktioner för att arbeta med mutexes
Fungera Beskrivning
pthread_mutex_init() Skapa en mutex [11] .
pthread_mutex_destroy() Mutex-destruktion [11] .
pthread_mutex_lock() Överföra en mutex till ett låst tillstånd (mutex capture) [11] .
pthread_mutex_trylock() Försök att sätta mutexet i blockerat tillstånd och returnera ett felmeddelande om tråden skulle blockera eftersom mutexen redan har en ägare [11] .
pthread_mutex_timedlock() Försök att flytta mutexet till det låsta tillståndet och returnera ett fel om försöket misslyckades före den angivna tiden [12] .
pthread_mutex_unlock() Överföra mutex till olåst tillstånd (släpp av mutex) [11] .

För att lösa specialiserade problem kan mutexes tilldelas olika attribut [11] . Genom attribut, med funktionen pthread_mutexattr_settype(), kan du ställa in typen av mutex, vilket kommer att påverka beteendet hos funktionerna för att fånga och släppa mutexet [13] . En mutex kan vara en av tre typer [13] :

Mutexes i C

C17 -standarden för programmeringsspråket C definierar en typ mtx_t[15] och en uppsättning funktioner som ska fungera med den [16] som måste vara tillgängliga om makrot __STDC_NO_THREADS__inte har definierats av kompilatorn [15] . Semantiken och egenskaperna för mutexes överensstämmer i allmänhet med POSIX-standarden.

Mutex-typen bestäms genom att skicka en kombination av flaggor till funktionen mtx_init()[17] :

Möjligheten att använda mutexes genom delat minne av olika processer beaktas inte i C17-standarden.

Mutexes i C++

C ++ 17-standarden för programmeringsspråket C++ definierar 6 olika mutex-klasser [20] :

Boost -biblioteket tillhandahåller dessutom namngivna mutexer och mutexer mellan processer, såväl som delade mutexer, som tillåter förvärv av en mutex för delat ägande av flera trådar av skrivskyddad data utan skrivexkludering under varaktigheten av låsförvärvet, vilket är i huvudsak en mekanism för läs- och skrivlås [25] .

Implementeringsdetaljer

På nivån för operativsystem

I det allmänna fallet lagrar mutex inte bara dess tillstånd, utan också en lista över blockerade uppgifter. Ändring av tillståndet för en mutex kan implementeras med hjälp av arkitekturberoende atomoperationer på användarkodnivå, men vid upplåsning av mutexen måste även andra uppgifter som blockerades av mutexen återupptas. För dessa ändamål är en synkroniseringsprimitiv på lägre nivå väl lämpad - futex , som implementeras på sidan av operativsystemet och tar på sig funktionaliteten av att blockera och avblockera uppgifter, vilket bland annat tillåter att skapa mutexes mellan processer [26] . I synnerhet, med hjälp av futex, implementeras mutex i Pthreads- paketet i många Linux- distributioner [27] .

På x86- och x86_64-arkitekturer

Enkelheten med mutex gör att de kan implementeras i användarutrymmet med hjälp av en assemblerinstruktion XCHGsom kan atomiskt kopiera mutexens värde till ett register och samtidigt ställa in mutexens värde till 1 (som tidigare skrivits till samma register). Ett mutex-värde på noll betyder att det är i låst tillstånd, medan ett värde på ett betyder att det är i olåst tillstånd. Värdet från registret kan testas för 0, och vid ett nollvärde ska kontroll återföras till programmet, vilket innebär att mutexet förvärvas, om värdet inte var noll så måste styrningen överföras till schemaläggaren för att återuppta arbetet med en annan tråd, följt av ett andra försök att förvärva mutex, som fungerar som en analog av aktiv blockering. En mutex låses upp genom att lagra värdet 0 i mutexet med kommandot XCHG[28] . Alternativt kan LOCK BTS(TSL-implementering för en bit) eller CMPXCHG[29] ( CAS- implementering ) användas.

Överföringen av kontroll till schemaläggaren är tillräckligt snabb för att det inte finns någon verklig aktiv vänteloop, eftersom CPU:n kommer att vara upptagen med att köra en annan tråd och inte kommer att vara ledig. Genom att arbeta i användarutrymme kan du undvika systemsamtal som är dyra i termer av processortid [30] .

I ARM-arkitekturen

ARMv7 - arkitekturen använder så kallade lokala och globala exklusiva monitorer för att synkronisera minnet mellan processorer, som är tillståndsmaskiner som styr atomaccess till minnesceller [31] [32] . En atomär läsning av en minnescell kan utföras med hjälp av instruktionen LDREX[33] , och en atomär skrivning kan göras genom instruktionen STREX, som också returnerar framgångsflaggan för operationen [34] .

Algoritmen för mutexinfångning innebär att man läser dess värde med LDREXoch kontrollerar det avlästa värdet för ett låst tillstånd, vilket motsvarar värdet 1 för mutexvariabeln. Om mutex är låst anropas koden för låsfrigöring. Om mutexen var i olåst tillstånd, kunde låsningen försökas med den skrivexklusiva instruktionen STREXNE. Om skrivningen misslyckas på grund av att mutexens värde har ändrats, upprepas infångningsalgoritmen från början [35] . Efter att ha fångat mutexet exekveras instruktionen DMB, vilket garanterar integriteten hos minnet för den resurs som skyddas av mutexen [36] .

Innan mutex släpps kallas instruktionen även DMB, varefter värdet 0 skrivs till mutex-variabeln med hjälp av instruktionen STR, vilket innebär överföring till olåst tillstånd. Efter att mutex har låsts upp, bör väntande uppgifter, om några, signaleras att mutex har släppts [35] .

Se även

Anteckningar

  1. Tanenbaum, 2011 , 2.3.6. Mutexes, sid. 165.
  2. Oleg Tsilyurik. Kärnprogrammeringsverktyg: Del 73. Parallellism och synkronisering. Lås. Del 1 . - www.ibm.com, 2013. - 13 augusti. — Tillträdesdatum: 2019-06-12.
  3. The Open Group Base Specifications Issue 7, 2018 edition, Rationale for System Interfaces, General  Information . Hämtad 20 juni 2020. Arkiverad från originalet 18 juni 2020.
  4. 1 2 3 C++17, 2017 , 33.4.3.4 Delade mutextyper, sid. 1373.
  5. Tanenbaum, 2011 , 2.3.6. Mutexes, sid. 165-166.
  6. ↑ 1 2 Steven Rostedt, Alex Shi. RT- mutex -implementeringsdesign - Linux-kärnan-dokumentationen  . Linux Kernel-dokumentationen . Utvecklingsgemenskapen för kärnor (7 juni 2017). Hämtad 16 juni 2020. Arkiverad från originalet 16 juni 2020.
  7. Skapa Mutex . Datum för åtkomst: 20 december 2010. Arkiverad från originalet den 14 februari 2012.
  8. ↑ 1 2 Michael Satran, Drew Batchelor. Kritiska  sektionsobjekt . Dokumentation . Microsoft (31 maj 2018). Datum för åtkomst: 20 december 2010. Arkiverad från originalet den 14 februari 2012.
  9. Michael Satran, Drew batchelor. Synkroniseringsfunktioner - Win32-appar  . Dokumentation . Microsoft (31 maj 2018). Hämtad 18 juni 2020. Arkiverad från originalet 18 juni 2020.
  10. Tanenbaum, 2011 , 2.3.6. Mutexes, Mutexes in Pthreads, sid. 167.
  11. 1 2 3 4 5 6 7 Tanenbaum, 2011 , 2.3.6. Mutexes, Mutexes in Pthreads, sid. 168.
  12. IEEE, Den öppna gruppen. pthread_mutex_timedlock  (engelska) . pubs.opengroup.org . Den öppna gruppen (2018). Hämtad 18 juni 2020. Arkiverad från originalet 18 juni 2020.
  13. ↑ 1 2 IEEE, Den öppna gruppen. pthread_mutexattr_settype(3  ) . Open Group Base Specifications Issue 7, 2018-utgåvan . Den öppna gruppen (2018). Datum för åtkomst: 20 december 2010. Arkiverad från originalet den 14 februari 2012.
  14. ↑ 1 2 3 IEEE, The Open Group. pthread_mutex_lock  (engelska) . Open Group Base Specifications Issue 7, 2018-utgåvan . Den öppna gruppen (2018). Hämtad 17 juni 2020. Arkiverad från originalet 17 september 2019.
  15. 1 2 C17, 2017 , 7.26 Trådar <threads.h>, sid. 274.
  16. C17, 2017 , 7.26.4 Mutex-funktioner, sid. 277-279.
  17. C17, 2017 , 7.26.4.2 Funktionen mtx_init, sid. 277-278.
  18. C17, 2017 , 7.26.1 Inledning, sid. 274.
  19. 1 2 C17, 2017 , 7.26.1 Inledning, sid. 275.
  20. C++17, 2017 , 33.4.3.2 Mutex-typer, sid. 1368.
  21. C++17, 2017 , 33.4.3.2.1 Klass mutex, sid. 1369-1370.
  22. C++17, 2017 , 33.4.3.2.2 Klass rekursiv_mutex, sid. 1370.
  23. C++17, 2017 , 33.4.3.3 Tidsinställda mutextyper, sid. 1370-1371.
  24. C++17, 2017 , 33.4.3.3.2 Class recursive_timed_mutex, sid. 1372-1373.
  25. Synkroniseringsmekanismer  . _ Boost C++ Libraries 1.73.0 . Hämtad 18 juni 2020. Arkiverad från originalet 18 juni 2020.
  26. Ulrich Drapper. Futexes Are Tricky  : [ arch. 2020-06-24 ] : [PDF]. - Red Hat, Inc., 2005. - 11 december.
  27. Karim Yaghmour, Jon Masters, Gilad Ben-Yossef, Philippe Gerum. Bygga inbyggda Linux-system: koncept, tekniker, knep och fällor . - "O'Reilly Media, Inc.", 2008. - S. 400. - 466 sid. - ISBN 978-0-596-55505-4 .
  28. Tanenbaum, 2011 , 2.3.6. Mutexes, sid. 166.
  29. Steven Rostedt. RT-mutex  implementeringsdesign . Linux Kernel-dokumentationen (7 juni 2017). Hämtad 30 augusti 2021. Arkiverad från originalet 13 augusti 2021.
  30. Tanenbaum, 2011 , 2.3.6. Mutexes, sid. 166-167.
  31. ARM, 2009 , 1.2.1 LDREX och STREX, sid. fyra.
  32. ARM, 2009 , 1.2.2 Exklusiva bildskärmar, sid. 5.
  33. ARM, 2009 , 1.2.1 LDREX och STREX, LDREX, sid. fyra.
  34. ARM, 2009 , 1.2.1 LDREX och STREX, STREX, sid. fyra.
  35. 1 2 ARM, 2009 , 1.3.2 Implementering av en mutex, sid. 12-13.
  36. ARM, 2009 , 1.2.3 Minnesbarriärer, sid. åtta.

Litteratur