User-Mode schemaläggning

Varning

Från och med Windows 11 stöds inte schemaläggning i användarläge. Alla anrop misslyckas med felet ERROR_NOT_SUPPORTED.

Schemaläggning i användarläge (UMS) är en enkel mekanism som program kan använda för att schemalägga sina egna trådar. Ett program kan växla mellan UMS-trådar i användarläge utan att systemschemaläggaren och återfå kontrollen över processorn om en UMS-tråd blockeras i kärnan. UMS-trådar skiljer sig från fibrer eftersom varje UMS-tråd har sin egen trådkontext i stället för att dela trådkontexten för en enda tråd. Möjligheten att växla mellan trådar i användarläge gör UMS effektivare än trådpooler för att hantera ett stort antal kortvariga arbetsobjekt som kräver få systemanrop.

UMS rekommenderas för program med höga prestandakrav som effektivt behöver köra många trådar samtidigt på system med flera processorer eller flera kärnor. För att kunna dra nytta av UMS måste ett program implementera en scheduler-komponent som hanterar programmets UMS-trådar och avgör när de ska köras. Utvecklare bör överväga om deras programprestandakrav motiverar arbetet med att utveckla en sådan komponent. Program med måttliga prestandakrav kan betjänas bättre genom att tillåta systemschemaläggaren att schemalägga sina trådar.

UMS är tillgängligt för 64-bitarsprogram som körs på AMD64- och Itanium-versioner av Windows 7 och Windows Server 2008 R2 via Windows 10 Version 21H2 och Windows Server 2022. Den här funktionen är inte tillgänglig på Arm64, 32-bitars versioner av Windows eller Windows 11.

Mer information finns i följande avsnitt:

UMS Schemaläggare

Ett programs UMS-schemaläggare ansvarar för att skapa, hantera och ta bort UMS-trådar och avgöra vilken UMS-tråd som ska köras. Programmets schemaläggare utför följande uppgifter:

  • Skapar en UMS-schemaläggartråd för varje processor där programmet ska köra UMS-arbetstrådar.
  • Skapar UMS-arbetstrådar för att utföra programmets arbete.
  • Har en egen kö med arbetstrådar som är redo att köras och väljer trådar som ska köras baserat på programmets schemaläggningsprinciper.
  • Skapar och övervakar en eller flera slutförandelistor där systemet köar trådar efter att bearbetningen i kerneln har slutförts. Dessa inkluderar nyligen skapade arbetstrådar och trådar som tidigare blockerats i ett systemanrop som avblockeras.
  • Tillhandahåller en ingångspunktfunktion för schemaläggaren för att hantera meddelanden från systemet. Systemet anropar startpunktsfunktionen när en scheduler-tråd skapas, en arbetstråd blockerar ett systemanrop eller en arbetstråd ger uttryckligen kontroll.
  • Utför rensningsuppgifter för arbetstrådar som har körts klart.
  • Utför en ordnad avstängning av schemaläggaren när programmet begär det.

UMS Scheduler-tråd

En UMS-schemaläggartråd är en vanlig tråd som har konverterat sig själv till UMS genom att anropa funktionen EnterUmsSchedulingMode. Systemschemaläggaren avgör när UMS-schemaläggartråden körs baserat på dess prioritet i förhållande till andra färdiga trådar. Processorn som scheduler-tråden körs på påverkas av trådens tillhörighet, samma som för icke-UMS-trådar.

Anroparen för EnterUmsSchedulingMode anger en slutförandelista och en UmsSchedulerProc startpunktsfunktion som ska associeras med UMS-schemaläggartråden. Systemet anropar den angivna startpunktsfunktionen när den har konverterat den anropande tråden till UMS. Scheduler-startpunktsfunktionen ansvarar för att fastställa lämplig nästa åtgärd för den angivna tråden. Mer information finns i UMS Scheduler Entry Point Function senare i det här avsnittet.

Ett program kan skapa en UMS-schemaläggartråd för varje processor som ska användas för att köra UMS-trådar. Programmet kan också ange tillhörigheten för varje UMS-schemaläggartråd för en specifik logisk processor, vilket tenderar att utesluta orelaterade trådar från att köras på processorn, vilket effektivt reserverar den för den scheduler-tråden. Tänk på att inställningen av trådtillhörighet på det här sättet kan påverka systemets övergripande prestanda genom att svälta andra processer som kan köras i systemet. Mer information om affinitet för trådar finns i Multiple Processors.

UMS-arbetstrådar, trådkontexter och slutförandelistor

En UMS-arbetstråd skapas genom att anropa CreateRemoteThreadEx- med attributet PROC_THREAD_ATTRIBUTE_UMS_THREAD och ange en UMS-trådkontext och en slutförandelista.

En UMS-trådkontext representerar UMS-trådtillståndet för en arbetstråd och används för att identifiera arbetstråden i UMS-funktionsanrop. Den skapas genom att anropa CreateUmsThreadContext.

En slutförandelista skapas genom att anropa funktionen CreateUmsCompletionList. En slutförandelista tar emot UMS-arbetstrådar som har slutfört körningen i kerneln och är redo att köras i användarläge. Endast systemet kan köa arbetstrådar till en slutförandelista. Nya UMS-arbetstrådar placeras automatiskt i kö till den slutförandelista som angavs när trådarna skapades. Tidigare blockerade arbetstrådar placeras också i kö till slutförandelistan när de inte längre blockeras.

Varje UMS-schemaläggartråd är associerad med en enda slutförandelista. Samma slutförandelista kan dock associeras med valfritt antal UMS-schemaläggartrådar, och en scheduler-tråd kan hämta UMS-kontexter från valfri slutförandelista som den har en pekare för.

Varje slutförandelista har en associerad händelse som meddelas av systemet när det placerar en eller flera arbetstrådar i kö till en tom lista. Funktionen GetUmsCompletionListEvent hämtar en referens till händelsen för en angiven slutförandelista. En applikation kan vänta på mer än en slutförandelistehändelse tillsammans med andra händelser som är relevanta för applikationen.

UMS Scheduler - startpunktsfunktion

Ett applikations ingångspunktsfunktion för schemaläggaren implementeras som en UmsSchedulerProc--funktion. Systemet anropar programmets startpunktsfunktion för schemaläggaren vid följande tidpunkter:

  • När en icke-UMS-tråd konverteras till en UMS-schemaläggartråd genom att anropa EnterUmsSchedulingMode.
  • När en UMS-arbetstråd anropar UmsThreadYield.
  • När en UMS-arbetstråd blockeras av en systemtjänst, till exempel ett systemanrop eller ett sidfel.

Parametern Reason för funktionen UmsSchedulerProc anger orsaken till att startpunktsfunktionen anropades. Om startpunktsfunktionen anropades på grund av att en ny UMS-schemaläggartråd skapades innehåller parametern SchedulerParam data som angetts av anroparen för EnterUmsSchedulingMode. Om startpunktsfunktionen anropades på grund av att en UMS-arbetstråd gav, innehåller parametern SchedulerParam data som anges av anroparen för UmsThreadYield. Om startpunktsfunktionen anropades på grund av att en UMS-arbetstråd blockerades i kerneln är parametern SchedulerParam NULL.

Scheduler-startpunktsfunktionen ansvarar för att fastställa lämplig nästa åtgärd för den angivna tråden. Om till exempel en arbetstråd blockeras kan funktionen på schemaläggarens startpunkt köra nästa tillgängliga beredda UMS-arbetstråd.

När funktionen scheduler entry point anropas bör programmets schemaläggare försöka hämta alla objekt i den associerade slutförandelistan genom att anropa funktionen DequeueUmsCompletionListItems. Den här funktionen hämtar en lista över UMS-trådkontexter som har slutfört bearbetningen i kerneln och är redo att köras i användarläge. Programmets schemaläggare bör inte köra UMS-trådar direkt från den här listan eftersom detta kan orsaka oförutsägbart beteende i programmet. I stället bör schemaläggaren hämta alla UMS-trådkontexter genom att anropa funktionen GetNextUmsListItem en gång för varje kontext, infoga UMS-trådkontexterna i schemaläggarens färdiga trådkö och först sedan köra UMS-trådar från den färdiga trådkön.

Om schemaläggaren inte behöver vänta på flera händelser bör den anropa DequeueUmsCompletionListItems med en tidsgränsparameter som inte är noll så att funktionen väntar på slutförandelistehändelsen innan den returneras. Om schemaläggaren behöver vänta på flera slutförandelistehändelser bör den anropa DequeueUmsCompletionListItems med en tidsgränsparameter på noll så att funktionen returnerar omedelbart, även om slutförandelistan är tom. I det här fallet kan schemaläggaren vänta explicit på slutförandelistehändelser, till exempel med hjälp av WaitForMultipleObjects.

UMS-trådkörning

En nyskapad UMS-arbetstråd placeras i kö till den angivna slutförandelistan och börjar inte köras förrän programmets UMS-schemaläggare väljer att köra den. Detta skiljer sig från icke-UMS-trådar, som systemschemaläggaren automatiskt schemalägger för körning, utom när anroparen uttryckligen skapar tråden i pausat läge.

Schemaläggaren kör en arbetstråd genom att anropa ExecuteUmsThread med arbetstrådens UMS-kontext. En UMS-arbetstråd körs tills den ger resultat genom att anropa UmsThreadYield funktion, blockerar eller avslutar.

METODTIPS FÖR UMS

Program som implementerar UMS bör följa dessa metodtips:

  • De underliggande strukturerna för UMS-trådkontexter hanteras av systemet och bör inte ändras direkt. Använd i stället QueryUmsThreadInformation och SetUmsThreadInformation för att hämta och ange information om en UMS-arbetstråd.
  • För att förhindra dödlägen bör UMS-schemaläggartråden inte dela lås med UMS-arbetstrådar. Detta omfattar både programskapade lås och systemlås som förvärvas indirekt av åtgärder, till exempel allokering från heapen eller inläsning av DLL:er. Anta till exempel att schemaläggaren kör en UMS-arbetstråd som läser in en DLL. Arbetstråden hämtar lastarlåset och blocken. Systemet anropar funktionen scheduler entry point som sedan läser in en DLL. Detta orsakar ett dödläge eftersom lastarlåset redan finns kvar och inte kan frigöras förrän den första tråden avblockeras. För att undvika problemet kan du delegera arbete som eventuellt delar lås med UMS-arbetstrådar till en dedikerad UMS-arbetstråd eller en icke-UMS-tråd.
  • UMS är mest effektivt när de flesta bearbetningar utförs i användarläge. Undvik att göra systemanrop i UMS-arbetstrådar när det är möjligt.
  • UMS-arbetstrådar bör inte förutsätta att systemschemaläggaren används. Detta antagande kan ha subtila effekter; Om en tråd i den okända koden till exempel anger en trådprioritet eller tillhörighet kan UMS-schemaläggaren fortfarande åsidosätta den. Kod som förutsätter att systemschemaläggaren används kanske inte fungerar som förväntat och kan brytas när den anropas av en UMS-tråd.
  • Systemet kan behöva låsa trådkontexten för en UMS-arbetstråd. Till exempel kan ett asynkront proceduranrop i kernelläge (APC) ändra kontexten för UMS-tråden, så trådkontexten måste vara låst. Om schemaläggaren försöker köra UMS-trådkontexten medan den är låst misslyckas anropet. Det här beteendet är avsiktligt och schemaläggaren bör utformas för att försöka få åtkomst till UMS-trådkontexten igen.