Systemavbrott laddar processorn: vad ska man göra. Deferred Procedure Call (DPC)


Ett vanligt problem med Windows-operativsystemet i alla utgåvor är laddningen av datorresurser genom "interna" processer. En av dessa processer är ett systemavbrott, som allvarligt kan belasta datorresurserna, vilket kommer att visas i Aktivitetshanteraren. Den vanligaste situationen är när ett systemavbrott laddar processorn, vilket gör att datorn allvarligt tappar prestanda. I den här artikeln kommer vi att titta på varför detta händer, samt om det är möjligt att inaktivera systemavbrott i Windows.

Systemavbrott: vad är denna process

Som standard körs systemavbrottsprocessen i Windows-operativsystemet konstant, men under normal drift bör den inte ladda systemkomponenter med mer än 5%. Om denna process mer allvarligt påverkar datorns resurser, indikerar detta närvaron av ett hårdvaruproblem, nämligen ett fel i driften av en av datorns komponenter.

När "System avbrott" laddar processorn kan detta signalera ett fel på grafikkortet, moderkortet, RAM-minnet eller något annat element i systemenheten. CPU:n försöker komplettera den saknade kraften på grund av en felaktig komponent med sina egna resurser, vilket framgår av "System Interrupts"-processen. Oftast är problemet med felaktig drift av datorkomponenter förknippat med den fullständiga eller partiella inkompatibiliteten hos det pågående programmet (eller spelet) med drivrutinerna för datorkomponenterna.

Hur man inaktiverar systemavbrott

Som nämnts ovan är systemavbrott inget annat än en indikator på att Windows dessutom kommer åt CPU-resurser. Att inaktivera systemavbrott för att förbättra datorns prestanda kommer inte att fungera, och du måste leta efter ett problem i driften av PC-komponenter. För att göra detta är det bekvämt att använda applikationen DPC Latency Checker, som kan laddas ner gratis på Internet från utvecklarens webbplats. Programmet låter dig identifiera felaktiga datorkomponenter.

För att diagnostisera systemet med applikationen DPC Latency Checker, kör den och vänta. Det kommer att ta lite tid att kontrollera datorn, varefter användaren ser på grafen om det finns problem i driften av systemkomponenterna. Applikationen kommer också att indikera möjliga fel och råder dig att leta efter dem genom att stänga av enheterna.

För att göra detta, gå till "Enhetshanteraren" genom att högerklicka på "Start" och välja lämpligt objekt, och börja stänga av enheterna en efter en. Efter varje avstängning, kolla i "Task Manager" och DPC Latency Checker-applikationen för att se om problemen med processorladdning av systemavbrott har lösts. Om problemet kvarstår slår du på enheten igen och går vidare till nästa.

Viktig: I processen med att inaktivera komponenter i "Enhetshanteraren", inaktivera inte "Dator", "Processor" och "System Devices", annars kommer detta att leda till en nödstart av datorn.

När en enhet hittas som, när den är frånkopplad, kommer att minska belastningen på processorn till det normala, uppdatera drivrutinerna för den här komponenten från utvecklarnas officiella webbplats.

Notera: Om försök har gjorts att inaktivera alla systemkomponenter, men Systemavbrottsprocessen fortsätter att ladda systemet, försök att uppdatera processordrivrutinerna.

I en situation där tipsen ovan inte hjälper till att hantera problemet med CPU-användning genom systemavbrott, kan du prova följande sätt att åtgärda situationen:

Det är värt att notera att du inte bör inaktivera systemavbrott via "Task Manager", detta kommer att krascha systemet, men kommer inte att lösa problemet.

Kontrollobjekt inkluderar primitiva objekt för trådar, avbrott, timers, synkronisering, profilering, samt två specialobjekt för implementering av DPC och APC. DPC-objekt (Deferred Procedure Call) används för att minska exekveringstiden för en ISR (Interrupt Service Routines) som utlöses av ett enhetsavbrott. Att begränsa mängden tid som spenderas på ISR:er minskar chansen att missa ett avbrott.

Systemhårdvaran tilldelar en hårdvaruprioritetsnivå till avbrott. Processorn kopplar också en prioritetsnivå till det arbete den utför. Processorn svarar bara på de avbrott som har högre prioritet än den använder för närvarande. Den normala prioritetsnivån (inklusive prioritetsnivån för alla användarlägen) är 0. Enhetsavbrott inträffar på nivå 3 eller högre, och en ISR för ett enhetsavbrott körs vanligtvis på samma prioritetsnivå som avbrottet (för att förhindra andra mindre viktiga avbrott från inträffade under bearbetning av ett viktigare avbrott).

Om ISR körs för länge kommer avbrott med lägre prioritet att försenas, vilket kan leda till dataförlust eller sakta ner system-I/O. Flera ISR:er kan köras vid varje given tidpunkt, där varje successiv ISR triggas av avbrott med progressivt högre prioritetsnivåer.

För att minska ISR-behandlingstiden utförs endast kritiska operationer, som att skriva resultaten av I/O-operationer och återinitiera enheten. Ytterligare bearbetning av avbrottet skjuts upp tills processorns prioritetsnivå sjunker och slutar blockera tjänsten för andra avbrott. DPC-objektet används för att representera det arbete som ska göras, och ISR anropar kärnnivån för att placera DPC:n på DPC-listan för en viss processor. Om DPC är först på listan, loggar kärnan en speciell processorhårdvaruavbrottsbegäran på nivå 2 (på vilken NT anropar DISPATCH-nivån). När den sista existerande ISR slutförs, sjunker processorns avbrottsnivå under 2, och detta avblockerar avbrottet för DPC-bearbetning. ISR för DPC-avbrottet kommer att bearbeta vart och ett av DPC-objekten (som kärnan har köat).

Tekniken att använda mjukvaruavbrott för att skjuta upp avbrottsbearbetning är en erkänd teknik för att reducera ISR-latens. UNIX och andra system började använda lat bearbetning på 1970-talet (för att hantera långsam hårdvara och begränsade seriella terminalbuffertar). ISR tog emot tecken från hårdvaran och ställde dem i kö. Efter att all avbrottsbearbetning på övre nivå slutförts, skulle ett mjukvaruavbrott utlösa en lågprioriterad ISR för teckenbearbetning (till exempel för att implementera återgången av markören med en position - för detta skickades ett kontrolltecken till terminalen för att radera det senast visade tecknet och markören flyttas bakåt).

Ett liknande exempel i ett modernt Windows-system är tangentbordet. Efter att en tangent har tryckts ned läser tangentbordets ISR nyckelkoden från registret och aktiverar sedan tangentbordsavbrottet igen, men bearbetar inte nyckeln omedelbart. Istället använder den DPC för att köa nyckelkodsbehandling (tills alla väntande enhetsavbrott har behandlats).

Eftersom DPC:er fungerar på nivå 2, hindrar de inte enhets-ISR:er från att exekvera, men de förhindrar att alla trådar körs tills alla köade DPC:er har slutförts och processorprioritetsnivån har fallit under 2. Enhetsdrivrutiner och systemet bör inte ta för lång tid för att exekvera ISR eller DPC. Eftersom trådar inte tillåts att köra kan ISR:er och DPC:er göra systemet långsamt och orsaka kraschar vid uppspelning av musik (att sakta ner de trådar som skriver musik från bufferten till ljudenheten). Ett annat vanligt användningsfall för DPC:er är att utföra timeravbrottsrutiner. För att undvika blockering av trådar bör timerhändelser (som måste köras under lång tid) köa förfrågningar till en pool av arbetartrådar (som kärnan underhåller för bakgrundsarbete).

Skicka ditt goda arbete i kunskapsbasen är enkelt. Använd formuläret nedan

Studenter, doktorander, unga forskare som använder kunskapsbasen i sina studier och arbete kommer att vara er mycket tacksamma.

FÖRKÄLLNING OCH FÖRKLARANDE ANM.

till kursprojektet i ämnet:

Application Profiler

1. Introduktion

2. Analytisk sektion

2.1. Teknisk uppgift

2.2. Översikt över Windows NT 5.x-arkitektur

2.3. Klassificering av förare

2.4. Allmän struktur för en äldre drivrutin

2.4.1. Procedur DriverEntry

2.4.2. Procedur för urladdning av drivrutiner

2.4.3. Arbetsrutiner för bearbetning av IRP-paket

2.4.3.1. IRP-pakethuvud

2.4.3.2. IRP-stack

2.4.3.3. Paketbehandlingsfunktion IRP_MJ_CREATE

2.4.3.4. Paketbehandlingsfunktion IRP_MJ_CLOSE

2.4.3.5. Paketbehandlingsfunktion IRP_MJ_DEVICE_CONTROL

2.4.4. ISR - Avbrottsrutin

2.4.5. DPC - fördröjt samtalsprocedur

3. Designdelen

3.1. Äldre drivrutin

3.1.1. Procedur DriverEntry

3.1.2. Ladda ner drivrutinen

3.1.3. DispatchCreate och DispatchClose

3.1.4. DispatchDeviceControl

3.2. Användarapplikation

4. Teknisk sektion

4.1. Val av operativsystem och programmeringsmiljö.

4.2. Gränssnitt

4.3. Systemkrav

5. Sammanfattning.

6. Lista över använd litteratur.

1. Introduktion

Mycket ofta, när man utvecklar programvara, blir det nödvändigt att övervaka dess arbete: hur länge dess trådar körs i kärnläge, hur mycket - i användarläge, hur mycket tid de spenderar på att vänta, såväl som antalet kontextväxlingar från ett läge till annan. Allt detta är viktigt, eftersom vart och ett av lägena har sina egna egenskaper. I kärnläge körs koden snabbare, men det finns risk för korruption av systemdata/kod. I motsats till kärnläge är användarläget begränsat i de tjänster som tillhandahålls så att dess kod inte kan krascha systemet. För samma ändamål utförs ytterligare kontroller i användarläge för att förhindra exekvering av skadliga instruktioner. Därför är exekveringshastigheten för användarlägeskod betydligt långsammare. Antalet kontextväxlar påverkar också hastigheten för kodexekvering, eftersom denna operation är ganska dyr (cirka 2000 cykler). Detta märktes tydligt när man utvecklade laboratoriearbeten och ett kursprojekt om datorgrafik: när man ritade en bild pixel för pixel med SetPixel-funktionen var ritningshastigheten oproportionerligt långsammare än när man använde en användarlägesbuffert, i vilken information om färgen på motsvarande element i buffertpixlarna matades gradvis in. Detta hände på grund av det faktum att när man använde SetPixel-funktionen fanns det två kontextväxlar (från användarläge till kärnläge och vice versa) per pixel, och när man använder en buffert som lagrar en kontextoberoende färgrepresentation, samma två växlar , men en gång per ritning hela ramen.

Således kommer möjligheten att ta reda på ovanstående statistiska information om målprogramvaran att låta dig i tid lägga märke till de så kallade "flaskhalsarna" i programmet, vilket hindrar förbättringen av applikationens övergripande prestanda.

2. Analytisk sektion

2.1 Teknisk uppgift

Syftet med det presenterade kursprojektet är att utveckla en enkel applikationsprofilerare som inkluderar:

Äldre drivrutin, som bör:

Regelbundet uppdatera information om processer och deras flöden;

Ge grundläggande information om processer och deras trådar;

Ange hårdvarukontexten för den valda tråden;

Se till att denna information nås säkert av flera användarklientapplikationer.

Användarapplikation som tillåter:

Installera och ta bort drivrutinen korrekt utan att behöva starta om systemet;

Kontakta föraren med frågor för diverse information.

2.2 Arkitektur översiktWindows NT 5.x

I Windows är applikationer separerade från själva operativsystemet. Dess kärnkod körs i privilegierat processorläge ( kärnläge), som ger åtkomst till systemdata och hårdvara. Applikationskoden körs i oprivilegierat processorläge ( användarläge) med en ofullständig uppsättning gränssnitt, begränsad tillgång till systemdata och ingen direkt tillgång till utrustning. När ett program i användarläge anropar en systemtjänst, avlyssnar processorn samtalet och växlar anropstråden till kärnläge. När systemtjänsten avslutas växlar operativsystemet trådkontexten tillbaka till användarläge och fortsätter dess exekvering.

Följande visar schematiskt den del av Windows-arkitekturen som påverkas av detta kursprojekt. Den specificerar inte komponenter som: systemstödsprocesser, tjänsteprocesser, miljöundersystem, hårdvaruabstraktionslager och stöd för fönster och grafik.

Användarapplikationer är en typ av användarprocess som körs i användarläge, där de är begränsade till att använda icke-privilegierade instruktioner.

Subsystem DLLs -- På Windows kan användarapplikationer inte anropa operativsystemtjänster direkt, istället arbetar de med en eller flera subsystem DLLs vars syfte är att översätta dokumenterade funktioner till motsvarande interna (och vanligtvis odokumenterade) systemtjänstanrop.

Det verkställande systemet innehåller de grundläggande operativsystemtjänsterna som tillhandahåller minne, process- och trådhantering, säkerhet, I/O och kommunikation mellan processer.

Kärnan -- innehåller operativsystemfunktioner på låg nivå som stöder till exempel trådschemaläggning, avbrott och undantagssändning. Den tillhandahåller också en uppsättning procedurer och grundläggande objekt som chefen använder för att implementera strukturer på högre nivå.

Drivrutiner används för att utöka funktionaliteten hos kärnan.

2.3 Klassificering av förare

Till skillnad från en användarapplikation är en drivrutin inte en process och har ingen körningstråd. Istället överförs kontrollen till föraren som ett resultat av en I/O-begäran från en användarapplikation eller drivrutin, eller som ett resultat av ett avbrott. I det första fallet är exekveringskontexten för drivrutinen exakt känd - det är ett applikationsprogram. I det andra fallet kan exekveringskontexten vara antingen känd eller slumpmässig - det beror på exekveringskontexten för den anropande förarens funktion. I det tredje fallet är exekveringskontexten slumpmässig, eftersom ett avbrott (och följaktligen exekvering av drivrutinskoden) kan inträffa under exekveringen av vilket applikationsprogram som helst.

Efter plats i förarstacken:

Drivrutiner på övre nivå -- ta emot förfrågningar från användarapplikationen och interagera med nedströmsdrivrutiner;

Mellanliggande drivrutiner -- ta emot förfrågningar från uppströmsförare och interagera med drivrutiner nedströms;

Drivrutiner på lägre nivå -- ta emot förfrågningar från drivrutiner på högre nivå, utför slutbehandling av förfrågningspaket.

Särskilj också konceptet monolitisk drivrutin- en förare på toppnivå som inte interagerar med några andra förare.

I samband med förbättringen av Windows Driver Model (WDM - Windows Driver Model), där Plug and Play-stöd och energibesparande teknologier lades till, började drivrutinerna delas in i:

Äldre drivrutiner (äldre drivrutiner, drivrutiner i "NT-stil") -- drivrutiner skrivna i gammal stil, utan stöd för innovationer;

WDM-drivrutiner - drivrutiner som uppfyller alla krav för den utökade WDM-modellen.

2.4 Allmän struktur för en äldre drivrutin

Den äldre drivrutinen har följande huvudingångar:

DriverEntry - procedur för att ladda drivrutinen;

DriverUnload - procedur för urladdning av föraren;

Arbetsrutiner för bearbetning av IRP-paket;

ISR-procedur (Interrupt Service Routine) - avbrottshanteringsprocedur;

DPC-procedur (Deferred Procedure Call) - procedur för ett försenat samtal.

2.4.1 Procedur DriverEntry

Denna procedur finns i alla drivrutiner och anropas av I/O-hanteraren när drivrutinen laddas.

Äldre drivrutiner utför mycket mer arbete i den än WDM-drivrutiner, eftersom de tvingas utföra arbetet med AddDevice-proceduren, som är obligatorisk för WDM-drivrutiner. Förutom att lösa initieringsuppgifter och registrera ingångspunkter för arbetsprocedurer för bearbetning av stödda IRP-paket och urladdningsprocedur för drivrutiner, här:

Definierar hårdvaran som drivrutinen kommer att kontrollera;

Enhetsobjekt skapas (IoCreateDevice-funktion) för varje fysisk eller logisk enhet som kontrolleras av denna drivrutin;

För enheter som ska vara synliga för användarapplikationer skapas symboliska länkar (funktion IoCreateSymbolicLink);

Vid behov ansluts enheten till avbrottsobjektet. Om en ISR-procedur kräver användning av en DPC-procedur, skapas och initieras objektet som motsvarar den i detta skede;

Tilldelning av minne som krävs för att föraren ska fungera.

2.4.2 Procedur för urladdning av drivrutiner

I/O-hanteraren anropar denna procedur när drivrutinen är dynamiskt urladdad. Denna procedur utför det omvända till vad som görs i DriverEntry-proceduren.

Äldre drivrutiner kännetecknas av följande steg:

För vissa typer av hårdvara är det nödvändigt att spara dess tillstånd i systemregistret, eftersom. under efterföljande förarladdning kan dessa data användas;

Om avbrott är aktiverade för enheten som betjänas, måste urladdningsproceduren inaktivera dem och koppla från avbrottsobjektet. Situationen när enheten kommer att generera avbrott för ett icke-existerande avbrottsobjekt kommer oundvikligen att leda till en systemkrasch;

Ta bort enhetsobjekt (IoDeleteDevice);

Frigör minne som tilldelats föraren under drift.

2.4.3 Arbetsrutiner för bearbetning av IRP-paket

Alla funktioner som registreras i DriverEntry-proceduren genom att fylla i MajorFunction-arrayen anropas av I/O-hanteraren för att behandla lämpliga förfrågningar från förarens klienter. Dessa förfrågningar formateras alltid som speciella datastrukturer - IRP-paket, vars minne tilldelas av I/O-hanteraren i den icke-sökta systempoolen. Strukturen för ett IRP-paket är sådan att det består av en rubrik med fast storlek och en IRP-stack vars storlek beror på antalet enhetsobjekt i stacken.

2.4.3.1 Paket IRP-huvud. Rubrikstrukturen för ett IRP-paket har följande fält:

IoStatus-fältet av typen IO_STATUS_BLOCK innehåller två underfält:

Status innehåller ett värde som drivrutinen ställer in efter bearbetning av paketet;

Informationen innehåller oftast antalet byte som skickas eller tas emot.

Fältet AssociatedIrp.SystemBuffer av typen PVOID innehåller en pekare till systembufferten om enheten stöder buffrad I/O;

MdlAddress-fältet av typen PMDL innehåller en pekare till en MDL-lista om enheten stöder direkt I/O;

Fältet UserBuffer av typen PVOID innehåller adressen till användarens I/O-buffert;

Fältet BOOLEAN Cancel är en indikation på att IRP bör avbrytas.

2.4.3.2 IRP-stack. Huvudsyftet med IRP-stackceller är att lagra funktionskod och I/O-begäranparametrar. För en begäran som är adresserad till drivrutinen på lägsta nivån har motsvarande IRP endast en stackplats. För en begäran som skickas till drivrutinen på toppnivå skapar I/O-hanteraren en IRP med flera stackplatser, en för varje enhetsobjekt.

Varje cell i IRP-stacken innehåller:

MajorFunction av typen UCHAR är en kod som beskriver syftet med operationen;

En MinorFunction av typen UCHAR är en kod som beskriver en sub-op-kod;

DeviceObject av typen PDEVICE_OBJECT är en pekare till enhetsobjektet som denna IRP-begäran riktades till;

FileObject av typen PFILE_OBJECT - filobjekt för denna begäran;

Parametrar för typ union - tillämpning beror på värdet på MajorFunction.

I/O-hanteraren använder MajorFunction-fältet för att hämta den procedur som behövs för att bearbeta begäran från MajorFunction-matrisen.

Varje procedur för att bearbeta IRP-paket måste ha följande parametrar:

Pekare till enhetsobjektet för vilket IRP-begäran är avsedd;

En pekare till en IRP som beskriver denna begäran;

2.4.3.3 Paketbehandlingsfunktion IRP_MJ_CREATE. Den här funktionen är utformad för att hantera förfrågningar om ett förarhandtag från användarapplikationer eller uppströmsdrivrutiner. Vanligtvis markerar denna funktion helt enkelt IRP-paketet som färdigt.

2.4.3.4 Paketbehandlingsfunktion IRP_MJ_CLOSE. Den här funktionen är utformad för att hantera förfrågningar om att stänga ett förarhandtag från användarapplikationer eller uppströmsdrivrutiner. Vanligtvis markerar denna funktion helt enkelt IRP-paketet som färdigt.

2.4.3.5 Paketbehandlingsfunktion IRP_MJ_DEVICE_CONTROL. Den här funktionen låter dig behandla avancerade förfrågningar från klienter i användarläge, som oftast används för att kommunicera mellan en applikation och en drivrutin. En sådan begäran kan genereras genom att anropa DeviceIoControl-funktionen från användarläge.

Den använder IOCTL-koder (I/O-kontrollkod), av vilka några är fördefinierade av operativsystemet, och några kan skapas av drivrutinsutvecklaren. Denna kod anges i begäran av I/O-hanteraren när ett IRP-paket genereras.

Drivrutinsoperationer som fungerar med IOCTL-förfrågningar kräver ofta att ett buffertområde ställs in för att hålla indata eller utdata. Det är möjligt att båda buffertarna används i en begäran.

Dataåtkomstmetoden som tillhandahålls av I/O Manager definieras i IOCTL-koden. Dessa metoder kan vara:

METHOD_BUFFERED: Användarinmatningsbufferten kopieras till systembufferten, och när bearbetningen är klar kopieras systembufferten till användarutgångsbufferten.

METHOD_IN_DIRECT: Krävda användarbuffertsidor laddas från disken till huvudminnet och låses. Vidare, med användning av DMA, överförs data mellan enheten och användaren.

METHOD_OUT_DIRECT: De nödvändiga användarbuffertsidorna laddas från disken till huvudminnet och låses. Vidare, med användning av DMA, överförs data mellan enheten och användaren.

METHOD_NEITHER: Denna överföringsmetod kontrollerar inte minnestillgänglighet, allokerar mellanbuffertar och genererar ingen MDL. IRP-paketet förmedlar buffertarnas virtuella adresser i I/O-begärarens adressutrymme.

I det här fallet spelar flaggorna som bestämmer typen av buffring i enhetsobjektet ingen roll när man arbetar med IOCTL-förfrågningar. Den buffrade utbytesmekanismen definieras varje gång ett IOCTL-värde specificeras i ett dedikerat fragment av denna datastruktur. Detta tillvägagångssätt ger maximal flexibilitet när du hanterar anropet DeviceIoControl anpassat läge.

Från förarens synvinkel nås buffertområden som innehåller data eller är avsedda för data med hjälp av följande strukturfält:

METHOD_IN_DIRECT eller METHOD_OUT_DIRECT

databuffert

Buffertadress i systemadressutrymme som anges i pIrp->AssociatedIrp.SystemBuffer

Klientens virtuella adress i Parametrar. DeviceIoControl. Typ3InputBuffer

Längden anges i Parameters.DeviceIoControl.InputBufferLength

databuffert

Använder buffring (systembuffert)

Adressen för bufferten i systemadressutrymmet anges i pIrp->AssociatedIrp. Systembuffert

Använder direktåtkomst, klientbuffert konverterad till MDL-lista, pekare som placeras i pIrp->MdlAddress

Klientens virtuella adress i pIrp->UserBuffer

Längden anges i Parameters.DeviceIoControl.OutputBufferLength

2.4.4 ISR - Avbrottsrutin

Föraren registrerar denna funktion så att den får kontroll i det ögonblick då hårdvaran som föraren servar skickar en avbrottssignal. Syftet med den här funktionen är att utföra minimalt arbete och registrera ett fördröjt samtal (DPC) för att betjäna avbrottet. Ett anrop från kärnavbrottshanteraren kan ske i alla sammanhang, antingen i kärnan eller i en användarprocess.

2.4.5 DPC - fördröjt samtalsprocedur

Sådana rutiner exekveras på en avbrottsbegärannivå (IRQL) lägre än ISR, vilket förhindrar att andra avbrott blockeras. De kan utföra allt eller slutföra det arbete som påbörjats i ISR ​​till serviceavbrott.

3. Design avsnitt

Så här ser interaktionsdiagrammet för användarapplikationen med drivrutinen genom systemkomponenterna ut:

3.1 Arv-förare

Följande procedurer är implementerade i Legacy-drivrutinen för detta kursprojekt:

DispatchCreate (bearbetar IRP_MJ_CREATE-paket);

DispatchClose (bearbetar IRP_MJ_CLOSE-paket);

DispatchDeviceControl (bearbetar IRP_MJ_DEVICE_CONTROL-paket).

3.1.1 Procedur DriverEntry

Här utförs typiska åtgärder för drivrutinsinitiering.

Förarens ingångspunkter är registrerade:

pDriverObject->DriverUnload = SpectatorDriverUnload;

PDRIVER_DISPATCH * majorFunction = pDriverObject->MajorFunction;

majorFunction[IRP_MJ_CREATE] = SpectatorDispatchCreate;

majorFunction[ IRP_MJ_CLOSE ] = SpectatorDispatchClose;

majorFunction[ IRP_MJ_DEVICE_CONTROL ] = SpectatorDispatchDeviceControl;

Ett enhetsobjekt skapas med namnet DEVICE_NAME:

#define DEVICE_NAME L"\\Device\\Spectator"

RtlInitUnicodeString(&enhetsnamn, DEVICE_NAME);

status = IoCreateDevice

sizeof(DEVICE_EXTENSION),

FILE_DEVICE_SPECTATOR,

FILE_DEVICE_SECURE_OPEN,

&pDeviceObject);

En symbolisk länk SYMBOLIC_LINK är registrerad för det skapade enhetsobjektet:

#define SYMBOLIC_LINK L"\\DosDevices\\Spectator"

RtlInitUnicodeString(&symbolicLink, SYMBOLIC_LINK);

status = IoCreateSymbolicLink(&symbolicLink, &deviceName);

Ett mutex-objekt för kärnan skapas:

NTSTATUS CreateMutex()

(BEGIN_FUNC(CreateMutex);

NTSTATUS status = STATUS_SUCCESS;

status = _ExAllocatePool(g_pMutex, NonPagedPool, sizeof(KMUTEX));

if (NT_SUCCESS(status))

(KeInitializeMutex(g_pMutex, 0);

status = STATUS_SUCCESS ;)

END_FUNC(CreateMutex);

return(status);)

För första gången laddas information om processer och deras trådar:

if (LockInfo() == STATUS_SUCCESS)

Funktionerna LockInfo() och UnlockInfo() är helt enkelt omslagsfunktioner för funktionerna LockMutex() och UnlockMutex() respektive. Den första av de två sista funktionerna väntar på ett kärnobjekt för en mutex.

Kärnmutex garanterar ömsesidigt exklusiv åtkomst till en enda resurs för trådar. Därav namnet på dessa objekt (ömsesidig uteslutning, mutex). De innehåller en användarräknare, en rekursionsräknare och en variabel som lagrar trådens ID. Mutexes beter sig precis som kritiska avsnitt. Men medan de senare är objekt i användarläge, är mutexes kärnobjekt. Dessutom låter ett enda mutex-objekt dig synkronisera åtkomst till en resurs med flera trådar från olika processer; i det här fallet kan du ställa in den maximala väntetiden för åtkomst till resursen.

Det är tack vare denna mutex som säkerhetskravet säkerställs vid åtkomst av lagrad information.

Timern initieras:

Timern är nödvändig för att uppdatera den lagrade informationen med ett visst intervall.

För att göra detta skapas ett "timer" kärnobjekt:

status = _ExAllocatePool(g_pTimer, NonPagedPool, sizeof(KTIMER));

KeInitializeTimerEx(g_pTimer, SynchronizationTimer);

Kommentar: minne för kärnobjekt bör tilldelas exklusivt i den icke-sökta poolen (sökord NonPagedPool).

Timers kan vara av två typer:

SynchronizationTimer - efter det angivna tidsintervallet eller nästa period överförs den till det signalerade tillståndet tills en av trådarna som väntar på den väcks. Därefter överförs timern till det icke-signalerade tillståndet.

NotificationTimer - efter det angivna tidsintervallet eller nästa period överförs den till signaltillståndet och alla trådar som väntar på den väcks. En sådan timer förblir i det signalerade tillståndet tills den uttryckligen ställs in på icke-signalerad.

För att göra lite användbart arbete med timern måste du registrera OnTimer() DPC-proceduren. För det måste du skapa ditt eget DPC-objekt, som med jämna mellanrum placeras i den systemomfattande kön:

status = _ExAllocatePool(g_pTimerDpc, NonPagedPool, sizeof(KDPC));

KeInitializeDpc(g_pTimerDpc, OnTime, NULL);

Dessutom, på grund av det faktum att åtgärder som kräver ett användarkontext måste utföras i denna drivrutin av timer, måste de tas bort från OnTimer ()-funktionen, som är en DPC-procedur, och därför är endast systemkontexten tillgänglig under dess avrättning. Det är emellertid nödvändigt att säkerställa att det nödvändiga arbetet utförs i rimlig synkronism med det ögonblick då DPC-funktionsobjektet hämtas från kön för bearbetning. För att göra detta, skapa en tråd som kommer att vara dedikerad till att vänta på någon händelse:

OBJECT_ATTRIBUTES objectAttributes;

InitializeObjectAttributes(&objectAttributes, NULL, OBJ_KERNEL_HANDLE,

status = PsCreateSystemThread(&hThread, THREAD_ALL_ACCESS, &objectAttributes,

NULL, NULL, UpdateThreadFunc, NULL);

KeInitializeEvent(g_pUpdateEvent, SynchronizationEvent, FALSE);

Kommentar: "Event"-kärnobjekten är identiska i typ med "timer"-kärnobjekten.

När denna händelse anländer kommer tråden att uppdatera systeminformation om processerna och deras trådar. Objektet för denna händelse kommer att överföras till signaltillståndet i OnTimer()-funktionen. Denna synkroniseringsmetod gjorde det möjligt att säkerställa att de nödvändiga åtgärderna utförs med ett specificerat intervall med en noggrannhet på en millisekund, vilket följer av följande meddelanden som fångas upp av DebugView-programmet från felsökningsversionen av drivrutinen:

0,00075233 ^^^^^^^^ OnTime ^^^^^^^^

0,00116579 ======== LockInfo ========

0,00118814 ======== ReloadInfo ========

0,99727142 ^^^^^^^^ OnTime ^^^^^^^^

1,00966775 ======== LockInfo ========

1,00968981 ======== ReloadInfo ========

1,99729049 ^^^^^^^^ OnTime ^^^^^^^^

2.05610037 ======== LockInfo ========

2.05632067 ======== ReloadInfo ========

2,99727035 ^^^^^^^^ OnTime ^^^^^^^^

2.99741030 ======== LockInfo ========

2.99743295 ======== ReloadInfo ========

3,99727631 ^^^^^^^^ OnTime ^^^^^^^^

3.99739385 ======== LockInfo ========

3.99741673 ======== ReloadInfo ========

4,99728107 ^^^^^^^^ OnTime ^^^^^^^^

4.99742365 ======== LockInfo ========

4.99744749 ======== ReloadInfo ========

5,99728870 ^^^^^^^^ OnTime ^^^^^^^^

5.99742651 ======== LockInfo ========

5.99744844 ======== ReloadInfo ========

Här är OnTime ögonblicket för inträde i OnTimer-timerproceduren, LockInfo är ögonblicket då tråden som ansvarar för att uppdatera informationen vaknar, ReloadInfo är ögonblicket då informationen faktiskt uppdaterades.

Som framgår av avlyssningen är frekvensen under de första två sekunderna inte på en hög nivå, men sedan stabiliseras situationen och noggrannheten förbättras som sagt till en millisekund.

Efter alla dessa steg startar äntligen timern:

LARGE_INTEGER dueTime = RtlConvertLongToLargeInteger(0);

BOOLEAN existerade = KeSetTimerEx(g_pTimer, dueTime, g_timerPeriod, g_pTimerDpc);

Här är dueTime tiden fram till det första anropet till OnTime()-proceduren, och g_timerPeriod är perioden för ytterligare anrop.

Slutligen, i DriverEntry-proceduren, kommer räknaren för användardefinierade klientapplikationer som fick handtaget för denna drivrutin att återställas: pDeviceExtension->clientCount = 0;

Enbart tack vare denna variabel blir det möjligt för flera användarapplikationer att komma åt drivrutinen samtidigt. Den enda begränsningen för dem är exklusiviteten för tillgång till information om processer och deras trådar.

3.1.2 Ladda ner drivrutinen

I denna procedur, om antalet drivrutinsklienter är lika med noll, raderas alla objekt som skapats för att organisera driften av timern, mutex, enhetsobjektet och dess symboliska länk tas bort. Om antalet klienter inte är noll, avlastas inte drivrutinen, eftersom det annars kommer att störa den normala driften av andra användar-klientapplikationer.

3.1.3 DispatchCreate och DispatchClose

Dessa funktioner håller reda på antalet öppna deskriptorer för en given drivrutin som erhålls med API-anropet CreateFile(). Hur många handtag har öppnats - samma nummer måste stängas av CloseHandle() API-anropet. Annars kommer drivrutinen att finnas kvar i operativsystemet i slutet av användarapplikationen, vilket naturligtvis är högst oönskat.

3.1.4 DispatchDeviceControl

Denna procedur betjänar IOCTL-förfrågningar från användarapplikationer som skickas av DeviceIoControl() API-anropet. I detta kursprojekt bygger interaktionen med föraren till största delen på deras applikation, förarens huvudfunktionalitet implementeras här: vad den var avsedd för. Därför är denna procedur den mest omfattande.

Först, oavsett den specifika IOCTL-begäran, erhålls en pekare till IRP-stackens plats för förarens enhetsobjekt:

PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp);

I den aktuella drivrutinen använder alla IOCTL-förfrågningar den buffrade dataöverföringsmetoden, eftersom deras volym är väldigt liten i alla förfrågningar. Med denna dataöverföringsmetod tilldelas tillräckligt med minne i systemets icke-sidpool för att passa fler av ingångs- och utgångsbuffertar. Innan bearbetning av en begäran kopieras innehållet i användarens inmatningsbuffert till systembufferten, och när det är klart, från systembufferten till användarens utgångsbuffert. Eftersom endast en systembuffert används för båda användarbuffertarna är det nödvändigt att vara försiktig när du bearbetar data, eftersom det är möjligt att skada olästa indata när du skriver, och då kommer de att gå förlorade för alltid.

Längden (i byte) på användarbuffertarna, input och output, hämtas från Parameters-fältet i IRP-stackcellen: Parameters.DeviceIoControl.InputBufferLength respektive Parameters.DeviceIoControl.OutputBufferLength. Och adressen till systembufferten extraheras från IRP-paketets rubrik: AssociatedIrp.SystemBuffer.

Helgenedata: [Nej]

Denna IOCTL-förfrågan används för att adressera föraren så att den ger ett svar på frågan om förfrågan är den enda klienten som arbetar med föraren för tillfället. Denna begäran skickas till föraren av varje användarapplikation när den är på väg att avslutas. Om svaret är ja, försöker den här applikationen att avsluta föraren, annars avslutas den helt enkelt, säker på att det finns andra klienter som arbetar med föraren och att den sista applikationen som avslutas kommer att ta hand om avlastningen av föraren.

ledig dagsedata: [Nej]

Den första IOCTL-begäran av denna är att användarapplikationen ska fånga systeminformation för exklusiv användning. En annan - respektive att frigöra denna resurs. De anropar helt enkelt funktionerna LockInfo() och UnlockInfo() med samma namn, som diskuterades tidigare när vi pratade om DriverEntry-proceduren i det här avsnittet.

ledig dagsedata: struktur med grundläggande information om processen.

Detta par av IOCTL-förfrågningar tillåter deras initiator att sekventiellt titta igenom strukturerna som beskriver de pågående processerna i systemet. Var och en av dem anropar funktionerna ProcessFirst() och ProcessNext() med samma namn. Den första funktionen ställer pekaren till den första posten och den andra flyttar pekaren till nästa, om det finns en. Resultatet av var och en av dessa funktioner är en fylld struktur med information om processen, såvida inte slutet av listan nås. I händelse av att slutet av listan ändå nås, markeras IRP-paketet ändå som framgångsrikt bearbetat, men värdet på antalet överförda bytes sätts till noll, vilket gör att användarapplikationen kan känna igen denna situation korrekt och sluta skicka ytterligare IOCTL_PROCESS_NEXT till föraren i tid -requests.

ledig dagsedata: struktur med grundläggande information om tråden.

Som i föregående stycke tillåter detta par av IOCTL-förfrågningar deras initiator att sekventiellt titta igenom strukturerna som beskriver trådarna i den valda processen. Logiken i att behandla dessa förfrågningar liknar att få information om processer.

3.1.4.6 IOCTL_OPEN_THREAD. Inmatningdata: behörigheter, unikt ID för måltråden.

ledig dagsedata: Ett handtag till målströmmen.

Vid bearbetning av denna IOCTL-begäran görs ett försök att öppna ett handtag till strömmen som har den specificerade identifieraren med de rättigheter som begärdes av användarklientapplikationen.

ledig dagsedata: [Nej].

Under behandlingen av denna IOCTL-begäran görs ett försök att stänga trådhandtaget som tidigare öppnats med en IOCTL_OPEN_THREAD-begäran.

3.1.4.7 IOCTL_GET_THREAD_CONTEXT. Inmatningdata: hårdvarukontextstruktur, måltrådsbeskrivning.

ledig dagsedata: hårdvarukontextstruktur.

Denna IOCTL-begäran får ut det mesta av DeviceIoControl API-anropet, eftersom både ingångs- och utgångsbuffertar är inblandade. Ingången är en struktur för hårdvarukontexten med CONTEXT::ContextFlags-fältet initierat, vilket indikerar vilka grupper av hårdvarukontextregister som ska returneras i denna struktur efter framgångsrikt slutförande av begäran. I detta projekt efterfrågas hela hårdvarukontexten.

3.2 Användarapplikation

Användarapplikationen innehåller två klasser: CDialog och CDriver. Som framgår av namnen ansvarar dessa klasser för interaktion med användaren genom applikationens dialogruta respektive interaktion med föraren, främst genom IOCTL-förfrågningar.

När en användarapplikationsinstans startar är det första den gör att försöka installera drivrutinen, om det inte redan har gjorts av en annan instans. Om installationen orsakade ett fel får användaren ett motsvarande meddelande, där orsaken till dess förekomst anges i textform, om den tillhandahölls, annars anges helt enkelt dess kod. Användaren kan begära installation av drivrutinen igen genom att ge ett positivt svar på lämpligt programförslag. Denna procedur kommer att upprepas tills drivrutinsinstallationen lyckas eller användaren vägrar att försöka igen.

Därefter laddas en rullgardinslista med pågående processer, sorterad alfabetiskt efter deras namn, den första processen väljs från listan och dess trådar visas redan i den andra rullgardinslistan. Dessa listor uppdateras varje gång användaren vill välja en annan process eller tråd, eftersom de behöver den senaste informationen för att göra det.

Denna information erhålls via drivrutinen, som redan nämnts, med hjälp av DeviceIoControl API-anropet:

BOOL DeviceIoControl

(HANTERA hDevice,

DWORD dwIoControlCode,

LPVOID lpInBuffer, DWORD nInBufferSize,

LPVOID lpOutBuffer, DWORD nOutBufferSize,

LPDWORD lpBytesReturned,

LPÖVERLAPPAD lpÖverlappad);

HANTERA hDevice - hantera till enheten för att skicka förfrågan till;

DWORD dwIoControlCode - IOCTL-begärankod;

LPVOID lpInBuffer - ingångsbuffertadress;

DWORD nInBufferSize - indatabuffertlängd;

LPVOID lpOutBuffer - utgångsbuffertadress;

DWORD nOutBufferSize - utdatabuffertlängd;

LPDWORD lpBytesReturned - antal byte returnerade;

LPOVERLAPPED lpOverlapped är en struktur som krävs när man använder asynkron frågekörning, vilket inte finns i den här applikationen.

Användningen av detta API-anrop är helt inkapslat i klassen CDriver, som implementerar en separat metod för varje begäran med ett namn nära namnet på IOCTL-begäran, vilket ger en intuitiv förståelse av gränssnittet för denna klass.

Denna klass kapslar också in användningen av Service Control Manager (SCM), som används för att dynamiskt installera, starta, stoppa och ta bort drivrutinen.

4. Teknisk sektion

4.1 Välja operativsystem och programmeringsmiljö

Windows valdes som operativsystem. Detta beror på det faktum att DOS-operativsystemet redan är föråldrat av många anledningar (vi har redan gått bort från single-tasking operativsystem), och det finns inga andra operativsystem för personliga maskiner med ett bra gränssnitt som verkligen är användar- vänlig. Windows är fortfarande det mest använda PC-operativsystemet. Dessutom har olika mjukvaruutvecklingsmiljöer utvecklats specifikt för Windows:

Visual C++, Visual Basic, Borland C++ Builder, Delphi och andra.

C++ valdes som språk för att skriva användarprogrammet. C++-språket ger mycket rika möjligheter för programmerare och är kanske det vanligaste språket i deras miljö. Det är ett mycket kraftfullt operatörsspråk. Dessutom ger det tillräcklig frihet att skriva program, medan Pascal sätter mycket snäva gränser, framför allt i beskrivningen av variabler och inte tillåter konstruktion av komplexa operatoruttryck. C valdes som språk för att skriva drivrutinen. Användningen av detta språk säkerställer portabilitet mellan systemen: det maximala som måste göras är att bygga om drivrutinen. Microsoft Visual Studio .Net valdes som utvecklingsmiljö, eftersom den tillhandahåller kraftfulla och bekväma verktyg inte bara för visuell utveckling av mjukvaruproduktens gränssnitt, utan också för att sätta upp projekt, vilket gör att du effektivt kan organisera din arbetsplats.

4.2 Gränssnitt

Så här ser profilfönstret för användarapplikationsinstansen ut:

Överst i dialogrutan finns två rullgardinslistor, varav den övre visar en lista över pågående processer i systemet, och den nedre - en lista över trådarna i denna process. Dessa kontroller låter dig tala om för din applikation vilken process och vilken tråd i den processen som ska övervakas.

Det finns tre grupper i dialogen:

Processinformationsgrupp:

ProcessID - process-ID;

ParentID - överordnad process-ID;

BasePriority - standardbasprioritet för processtrådar;

ThreadCount - antalet trådar i processen;

KernelTime - total tid spenderad i kärnläge av processtrådar, 1 enhet motsvarar 100 ns;

UserTime - total tid spenderad i användarläge av processtrådar, 1 enhet motsvarar 100 ns.

Flödesinformationsgrupp:

ThreadID - trådidentifierare;

BasePriority - basprioritet för tråden;

Prioritet - trådprioritet;

ContextSwitches - antal kontextväxlar som utförs av tråden;

KernelTime - tid spenderad i kärnläge (1 enhet motsvarar 100 ns);

UserTime - tid i användarläge (1 enhet motsvarar 100 ns).

WaitTime - det ögonblick då tråden gick in i vänteläge (nedräkningen är från det ögonblick då systemet startades).

Flödeskontextgrupp:

Här är trådens hårdvarukontext. De flesta applikationer förväntar sig input från användaren. När du övervakar trådarna i en sådan process kanske du inte ser några förändringar alls. Därför, för en mer visuell vy, är det värt att köra uppgifter som kräver stora beräkningskostnader. Till exempel WinAmp, som du kan spela musik med - strömmen som är ansvarig för detta syns omedelbart genom att ändra de allmänna registren. Men de vanligaste ändringarna i register för olika ändamål sker i riktigt "tungviktiga" uppgifter, till exempel kan du ta ett kursprojekt om Datorgrafik.

4.3 Systemkrav

Drivrutinen är skriven för Windows NT version 5.x.

Hantering av förfrågningar från flera anpassade klientapplikationer har endast testats på Windows XP Service Pack 2.

Slutsats

Som ett resultat av arbetet med projektet implementerades en användarapplikation som interagerar med Legacy-drivrutinen. Med den får den grundläggande information om den valda processen, grundläggande information och hårdvarukontexten för den valda tråden i den angivna processen. Denna applikation är grunden för implementeringen av fullfjädrade applikationsprofiler för att spåra målapplikationer och för att upptäcka flaskhalsar i dem, vilket avsevärt kan öka effektiviteten för programmeraren och programvaran han utvecklar.

Lista över begagnad litteratur

1. V.P. Soldatov "Programmera Windows-drivrutiner". Ed. 3:e, reviderad. och ytterligare - M.: Binom-Press LLC, 2006 - 576 s.: ill.

2. M. Russinovich, D. Solomon "Microsoft Windows Internals: Windows Server 2003, Windows XP and Windows 2000", 4:e upplagan.

3. J. Richter "Windows för proffs: skapa effektiva Win32-applikationer, med hänsyn till detaljerna i 64-bitarsversionen av Windows" / Per, engelska - 4:e upplagan. - St. Petersburg; Peter; M .: Förlags- och handelshuset "Russian Edition", 2001.

4. Schreiber, Sven B., 1958-Odokumenterade Windows 2000-hemligheter: en programmerares kokbok.

5. Garry Nebbett, Windows NT/2000 Native API.

Liknande dokument

    De viktigaste fördelarna med modulär programmering. Val av procedur: inmatning av en array från konsolen, utdata till arrayens skärm, information om författaren och tillståndet för det lösta problemet före bearbetning och efter bearbetning. Hierarki av procedurer, kännetecknande för syftet med modulerna.

    abstrakt, tillagt 2016-01-29

    Studera konceptet, vektorerna och mekanismerna för avbrottshantering; deras klassificering beroende på ursprungskällan. Funktioner för svaret från hårdvaru- och mjukvarudelar i operativsystemet på signaler om förekomsten av någon händelse i datorn.

    abstrakt, tillagt 2011-06-22

    Analys av befintlig teknik för att skapa webbapplikationer. Utveckling av en nätverksteknik för publicering och bearbetning av information om barn i dagis nr 176 "Squirrel" med hjälp av JSP-sidor och servlets med hjälp av en JDBC-drivrutin för att komma åt databasen.

    terminsuppsats, tillagd 2011-12-18

    Utveckling av datalagring och bearbetningssystem, gränssnitt. Använder Xamarin.Forms teknologi för att organisera ifyllningen av fraktsedlar. Val av operativsystem, programmeringsspråk och miljö. Hårdvaruintegrering av informationssystemet.

    avhandling, tillagd 2017-09-07

    Principer och algoritmer för avbrottshantering. En uppsättning åtgärder för att implementera stegen i. Utveckling av invånarprogrammets struktur och algoritm. Implementering av programmet i Assembly språk, metoder för dess felsökning och testning.

    terminsuppsats, tillagd 2014-12-22

    Ett exempel på att bygga ett program med aritmetiska operatorer. Grundläggande verktyg för att skapa en miniräknare. Procedur för inmatning av siffror. Ändrad procedur för hantering av tryckning av "+"-knappen. Proceduren för att öppna formuläret "Hjälp", det slutliga resultatet.

    presentation, tillagd 2012-02-03

    Designa en avbrottshanteringsmekanism. Intel 82C59A avbrottskontroller. Avbryt I/O. Programmerbar gränssnittskontroller Intel 82C55A. Processorns roll vid I/O-avbrottshantering. Översikt över avbrottshanteringsalgoritmen.

    test, tillagt 2010-05-19

    Jämförelse av resultaten av simuleringsmodellering och analytisk beräkning av egenskaper. Undersökning av datapaketskopplingsnoden, paketbearbetning i processorn, buffring och överföring över utgångsledningen. Bestämning av processorns belastningsfaktor.

    terminsuppsats, tillagd 2011-06-29

    Krav och struktur på system för ekonomisk informationsbehandling. Informationsteknik och systemunderhåll, informationsskydd. Processen att skapa frågor, formulär, rapporter, makron och moduler. Verktyg för att organisera databaser och arbeta med dem.

    terminsuppsats, tillagd 2012-04-25

    Grundläggande metoder för att arbeta i programmeringsmiljön Delphi. Teknikens egenskaper för att skapa enkla applikationer. Arbeta med applikationsutvecklingsmiljökomponenter. Inmatning, redigering, urval och utmatning av information. Aspekter av att använda grenstrukturen.

En viktig egenskap hos avbrottsbegärda rutiner är att de utför arbete som oftast inte har med den aktuella processen att göra.

Till exempel kan en diskdrivrutin ta kontroll efter att diskstyrenheten har skrivit information mottagen från process A till motsvarande sektorer, men detta ögonblick sammanfaller inte med perioden för nästa iteration av exekveringen av process A eller dess tråd.

I det mest typiska fallet kommer process A att vänta på att en I/O-operation ska slutföras (i en synkron I/O-operation) och diskdrivrutinen kommer att avbryta någon annan process.

I vissa fall är det i allmänhet svårt att entydigt avgöra för vilken process en viss OS-mjukvarumodul, såsom en trådschemaläggare, fungerar. Därför införs begränsningar för sådana procedurer - de har inte rätt att använda de resurser (minne, öppna filer etc.) som den aktuella processen fungerar med.

Avbrottsrutiner arbetar med de resurser som tilldelades dem när motsvarande drivrutin initierades eller själva operativsystemet initierades. Dessa resurser tillhör operativsystemet, inte till en specifik process. Så minne för drivrutiner allokeras från systemområdet. Därför är det vanligt att säga att avbrottshanterare verkar utanför ramen för en process.

Avbrottsschemaläggning är en viktig funktion i OS, och denna funktion är implementerad i nästan alla flerprograms OS. Som regel implementerar operativsystemet en arbetsschemaläggningsmekanism på två nivåer. Den översta nivån av schemaläggning utförs av avbrottshanteraren, som allokerar CPU-tid till en ström av inkommande avbrottsbegäranden av olika typer - extern, intern och mjukvara. Den återstående processortiden fördelas av en annan dispatcher, tråddispatchern, baserat på kvantiseringsdisciplinerna och andra som vi har övervägt.

Systemsamtal

Ett systemanrop tillåter en applikation att be operativsystemet att utföra någon åtgärd, presenterad som en procedur (eller en uppsättning procedurer) för OS-kodsegmentet.

För en applikationsprogrammerare ser operativsystemet ut som något slags bibliotek som implementerar användbara funktioner som gör det lättare att hantera en applikationsuppgift eller utföra åtgärder som är förbjudna i användarläge, som att kommunicera med en I/O-enhet.

Implementeringen av systemanrop måste uppfylla följande krav:

Ge växling till privilegierat läge;

har en hög hastighet att anropa OS-procedurer;

· tillhandahålla enhetlig åtkomst till systemanrop för alla hårdvaruplattformar som operativsystemet körs på;

möjliggör enkel utökning av uppsättningen av systemsamtal;

Ge OS-kontroll över korrekt användning av systemanrop

I de flesta operativsystem hanteras systemsamtal på ett centraliserat sätt baserat på existensen av en systemsamtalsförare.

På alla systemanrop, exekverar applikationen ett mjukvaruavbrott med ett specifikt och unikt vektornummer.

Innan ett programavbrott körs skickar applikationen systemets anropsnummer till operativsystemet. Metoden för överföring beror på implementeringen. Till exempel kan ett nummer placeras i ett specifikt processorregister eller skickas till stacken. Systemanropsargument skickas också på något sätt, de kan komma i vägen, både i allmänna register, och passera genom stacken eller arrayen, RAM.

Proceduradress 21h
Proceduradress 22h
Proceduradress 23h
Systemanropssamordnarens adress
Systemanropssamordnare
Behandlingsprocedur för systemsamtal 21h
Systemsamtalshanteringsprocedur 22h
Behandlingsprocedur för systemsamtal 23h

När systemanropet slutförs återgår kontrollen till avsändaren och avsändaren får också utgångskoden för det samtalet. Avsändaren återställer processorregistren, placerar en returkod i ett specifikt register och exekverar en avbrottsreturinstruktion som återställer processorn till icke-privilegierat läge.

Det beskrivna tabellformade sättet att organisera systemanrop accepteras i nästan alla operativsystem. Det låter dig enkelt ändra sammansättningen av systemsamtal genom att helt enkelt lägga till en ny adress i tabellen och utöka utbudet av giltiga samtalsnummer.

OS kan göra systemanrop synkron eller asynkron lägen.

Synkront systemanrop innebär att processen som gjorde ett sådant samtal avbryts tills systemanropet har utfört allt som krävs . Schemaläggaren sätter sedan processen i redoläge.

Asynkront systemanrop inte försätter processen i viloläge efter att någon initial systemåtgärd har utförts, såsom initiering av en I/O-operation, återgår kontrollen till applikationsprocessen.

De flesta systemanrop i operativsystemet är synkrona.

Medan kärnlägeskoden med högsta prioritet körs kan ingen annan kod på den processorn starta. Naturligtvis, om för stora mängder programkod exekveras vid för höga IRQL-värden, kommer detta oundvikligen att leda till en allmän försämring av systemet.

För att lösa denna typ av problem måste kärnlägeskod utformas för att undvika långvarig körning vid förhöjda IRQL. En av de viktigaste komponenterna i denna strategi är Deferred Procedure Calls (DPC) - deferred procedure calls.

DPC:s funktion

Schemat för att använda uppskjutna proceduranrop låter dig bygga exekveringsprocessen på ett sådant sätt att uppgiften kan planerad kod körs med hög IRQL, men det har den inte ännu genomförde . Detta uppskjutande är tillämpligt om ett avbrott servas i drivrutinen och det inte finns någon anledning att blockera annan lägre IRQL-kod från att exekvera. Med andra ord när bearbetningen av en given situation smärtfritt kan överföras till en senare tidpunkt.

Operativsystemet upprätthåller en kö av DPC-objekt för att ta hänsyn till DPC-anropsbegäranden.

Till att börja med begränsar vi oss till att överväga ett enklare fall att arbeta med DPC-procedurer avsedda att användas i samband med avbrottshanteringsprocedurer. Denna typ av DPC-procedurer har fått ett speciellt namn DpcForIsr i litteraturen.

Ett DPC-objekt för användning i avbrottsrutiner skapas vid ett samtal IoInitializeDpcRequest, vilket vanligtvis utförs i förarens startprocedurer. Detta anrop registrerar proceduren som tillhandahålls av DpcForIsr-drivrutinen och associerar den med objektet som skapas - en ganska vanlig teknik i Windows. Det bör särskilt noteras att DPC-objektet som skapas av detta anrop kommer att finnas kvar i operativsystemets tarmar, oåtkomligt för drivrutinsutvecklaren. (Skillnaden mellan DpcForIsr och andra DPC-procedurer är bara att arbetet med de senare sker med anrop Ke...Dpc, och DPC-objekten som skapats för dem är tillgängliga för drivrutinsutvecklaren.)

Om föraren har registrerat sin egen DpcForIsr-procedur kan motsvarande DPC-objekt under bearbetningen av ISR-avbrottet av proceduren placeras i DPC-systemkön (i själva verket en begäran om att anropa denna DpcForIsr-procedur senare) - genom att anropa IoRequestDpc. DpcForIsr-proceduren och kommer senare att slutföra bearbetningen av den mottagna ISR-en genom begäran-proceduren, som kommer att utföras under mindre kritiska förhållanden och med låg IRQL.

Generellt sett består funktionen av DPC-procedurer (i det här fallet DpcForIsr) av följande operationer:

  • När någon del av kod som körs med en hög (hårdvara) IRQL vill schemalägga en del av sitt arbete för att göras vid en låg IRQL, lägger den till ett DPC-objekt till systemets uppskjutna proceduranropskö.
  • Förr eller senare sjunker processorns IRQL-värde under DISPATCH_LEVEL, och arbetet som skjutits upp av avbrottet betjänas av DPC-funktionen. DPC-hanteraren hämtar varje DPC-objekt från kön och anropar motsvarande funktion, en pekare till vilken är lagrad i detta objekt. Denna funktion anropas medan processorn körs på DISPATCH_LEVEL.

Funktioner hos DPC-mekanismen

Som regel är det inte svårt att arbeta med uppskjutna proceduranrop, eftersom operativsystemen Windows 2000/XP/Server 2003 erbjuder en stor uppsättning systemanrop som döljer de flesta detaljerna i denna process. Två av de mest vilseledande aspekterna av att arbeta med DPC bör dock särskilt lyftas fram.

För det första sätter Windows NT 5 en gräns för att en instans av ett DPC-objekt kan placeras i systemets DPC-kö under en given tid. Försök att sätta i DPC-kön ett objekt som exakt matchar ett som redan finns avvisas. Som ett resultat inträffar endast ett DPC-proceduranrop, även om föraren förväntar sig att två anrop ska göras. Detta kan hända om två avbrott har anropats av en servad enhet och det första pågående proceduranropet ännu inte har börjat bearbetas. Den första instansen av DPC-objektet är fortfarande i kön, medan föraren redan har börjat bearbeta det andra avbrottet.

Utformningen av drivrutinen bör tillhandahålla en sådan funktion av DPC-mekanismen. Det kan vara nödvändigt att tillhandahålla en ytterligare DPC-förfrågningsräknare, eller så kan föraren implementera sin egen implementering av förfrågningskön. Medan den faktiska uppskjutna proceduren körs kan du kontrollera räknaren och din egen förfrågningskö för att avgöra vilket specifikt arbete som ska utföras.

För det andra finns det betydande synkroniseringsproblem när man kör på multiprocessorplattformar. Låt oss anta att programkoden, som körs på en processor, utför en avbrottstjänst och schemalägger ett DPC-proceduranrop (genom att placera ett DPC-objekt i systemkön). Emellertid, även innan avbrottet har fullbordats, kan en annan processor börja bearbeta DPC:n för objektet placerat i systemkön. Sålunda uppstår en situation där avbrottstjänstprogramkoden exekveras parallellt och samtidigt med procedurens DPC-kod. Av denna anledning är det nödvändigt att tillhandahålla åtgärder för att tillförlitligt synkronisera åtkomsten av DPC-procedurkoden till resurser som används i samband med förarens avbrottsserviceprocedur.

Om du tittar noga på listan över anropsparametrar IoInitializeDpcRequest och IoRequestDpc(designad för att fungera med DpcForIsr-procedurer), är det lätt att se att DPC-objektet är "fäst" till enhetsobjektet. När detta objekt placeras i DPC-kön vid tidpunkten för ISR-proceduren, specificeras även enhetsobjektet. Detta är hur säkerhet uppnås, vilket specifikt DPC-proceduranrop "beställs" av ISR-proceduren (korrelation per enhetsobjekt). Detta innebär också att en drivrutin som har skapat flera enhetsobjekt (ett ganska sällsynt fall) också kan använda flera DpcForIsr-procedurer - en för varje enhetsobjekt.

Systemets DPC-mekanism förhindrar samtidig behandling av DPC-objekt från systemkön, även i en multiprocessorkonfiguration. Således, om resurser delas av flera uppskjutna procedurer, så finns det ingen anledning att oroa sig för att synkronisera åtkomst till dem.

Ovan diskuterade vi användningen av DPC-rutiner för att slutföra avbrottsbehandling, det vill säga DpcForIsr. DPC-procedurer kan dock även användas på andra sätt, till exempel i samband med timers för att organisera väntan. För att göra detta skapas ett DPC-objekt genom att anropa KeInitializeDPC A som associerar detta objekt med DPC-proceduren som är en del av drivrutinen. Efter det kan du ställa in timeout i den förinitierade (med KeInitializeTimer eller KeInitializeEx) timerobjekt. Samtalet används för att ställa in timeoutintervallet. KeSetTimer, som, som en av parametrarna, måste skickas en pekare till ett initierat DPC-objekt. Efter att DPC-timeouten löper ut kommer objektet att läggas in i systemets DPC-kö, och DPC-proceduren som är associerad med det kommer att anropas så snart som möjligt. DPC-procedurer av denna andra typ betecknas i DDK-dokumentationen med termen "Custom DPC". (Detta användningsfall av DPC-procedurer gör dem mycket lika APC-anrop i användarläge.)

För att placera objekt som motsvarar den andra typen av DPC-procedurer (ej relaterade till avbrott) i systemets DPC-kö bör du använda anropet KeInsertQueueDpc. Följaktligen måste samtalsinitieringskoden fungera på en IRQL-nivå av minst DISPATCH_LEVEL.

För att rensa systemets DPC-kö från anpassade DPC-procedurer, till exempel, om föraren behöver avsluta omedelbart, är samtalet avsett KeRemoveQueueDpc, som kan anropas från kod på vilken IRQL-nivå som helst.







2022 gtavrl.ru.