Spin-blocarea, arhitectura generală a ferestrelor nt, articole, programare - programare c,

Spin-lock este cel mai simplu mecanism de sincronizare. Spin-lock poate fi capturat. și eliberat. Dacă spinlock-ul a fost capturat, o încercare ulterioară de a captura spinlock-ul de orice fir va avea ca rezultat o buclă infinită care încearcă să capteze starea spin-lock (ocupată în așteptare). Ciclul se va termina numai atunci când proprietarul anterior al blocajului de rotire îl eliberează. Utilizarea blocărilor de centrifugare este sigură pe platformele multiprocesor, adică se garantează faptul că, chiar dacă este solicitat simultan de două fire pe două procesoare, numai un fir va fi capturat.

Spin-blocarea este concepută pentru a proteja datele care sunt accesate pe diverse niveluri, inclusiv nivelurile IRQL ridicate. Acum, imaginați-vă următoarea situație: codul care ruleaza la nivelul LEVEL ICCV PASSIVE_ a luat un sistem de blocare de spin pentru schimbare în condiții de siguranță ulterioară unele date. Codul a fost întrerupt cod cu mai mare ICCV DISPATCH_LEVEL, care a încercat să captureze aceeași blocare de spin, și rezultă din descrierea de spin-blocare, a intrat într-o buclă infinită de așteptare de presă de blocare. Acest ciclu nu se va termina, deoarece codul care surprinde spinlock și ar trebui să fie liber, are un ICCV mai mic și nu avea șansa de a efectua! Pentru o astfel de situație nu a apărut, un mecanism care nu permite acest cod cu un anumit nivel de cod ICCV de întrerupere cu ICCV inferioară, la momentul în care codul cu ICCV inferior deține spinlock. Un astfel de mecanism este de a crește nivelul actual la momentul de captare ICCV spinlock ICCV la un anumit nivel, asociat cu spinlock, și restaurarea nivelului de ICCV vechi la momentul eliberării. Rezultă că codul care rulează la un nivel ridicat de ICCV, nu are dreptul de a avea acces la resursa protejată de spin-blocare în cazul în care nivelul de ICCV spinlocks nivel inferior ICCV producătoare de acces la codul de resurse. Când încercați acest cod pentru a captura de spin nivelul de blocare ICCV va fi coborâtă la nivelul spinlocks ICCV, ceea ce va duce la consecințe imprevizibile.
În NT, există două tipuri de încuietori:

  • Încuietori normale de spin, caz special în care sunt anularea cererii de I / O de blocare a tipului de spin, folosită când așteptați cereri de intrare / ieșire (consultați secțiunea "Anularea cererilor I / O").
  • Spin-blocarea temporizării întreruperii.

Cu ajutorul blocărilor obișnuite, IRQL DISPATCH_LEVEL este conectat, adică:

  • toate încercările de a le capta ar trebui să fie efectuate la nivelul IRQL, mai mic sau egal cu DISPATCH_LEVEL;
  • În cazul blocării spinlock-ului, nivelul IRQL actual se ridică la nivelul DISPATCH_LEVEL.

Unul dintre nivelurile de DIRQL este asociat cu blocarea rotației de sincronizare întreruptă. Utilizarea blocărilor normale de rotire va fi descrisă mai jos (cu excepția blocărilor de centrifugare pentru anularea cererilor de intrare / ieșire care au fost descrise în secțiunea anterioară). Utilizarea blocărilor de centrifugare pentru sincronizarea întreruperilor va fi descrisă în secțiunea despre manipularea întreruperilor.

Folosind încuietori convenționale de rotire

  1. 1. VOID KeInitializeSpinLock (IN PKSPIN_LOCK SpinLock); Această funcție inițializează obiectul kernel KSPIN_LOCK. Memoria pentru blocarea spin-ului ar trebui deja să fie alocată în memoria ne-paginată.
    2. VOID KeAcquireSpinLock (IN PKSPIN_LOGK SpinLock, OUT PKIRQL Oldlrql); Această funcție captează blocarea roților. Funcția nu va readuce controlul până când încuietoarea de blocare nu reușește. La sfârșitul funcției, nivelul IRQL se ridică la nivelul DISPATCH_LEVEL. Al doilea parametru returnează nivelul IRQL, care era înainte ca blocarea să fi fost capturată (ar trebui să fie <= DISPATCH_LEVEL).
    3. VOID KeReleaseSpinLock (IN PKSPINJLOCK SpinLock, OUT PKIRQL Newlrql); Această funcție eliberează spinlock-ul și stabilește nivelul IRQL la valoarea parametrului Newlrql. Aceasta ar trebui să fie valoarea returnată de funcția KeAcquireSpinLock () din parametrul Oldlrql.
    4. VOID KeAcquireLockAtDpcLevel (IN PKSPIN_LOCK SpinLock); Această funcție optimizată captează spinlock-ul cu cod care rulează deja la nivelul IRQL DISPATCH_LEVEL. În acest caz, nu este necesară modificarea nivelului IRQL. Pe o platformă uniprocesor, această funcție nu face nimic, deoarece sincronizarea este furnizată de arhitectura IRQL în sine.
    5. VOID KeReleaseLockFromDpcLevel (IN PKSPIN_LOCK SpinLock); Această funcție eliberează spinlock-ul cu codul care a capturat blocarea utilizând funcția KeAcquireLockAtDpcLevel (). Pe o platformă uniprocesor, această funcție nu face nimic.

Exemplu de utilizare a încuietori convenționale de rotire:

typedef struct _DEVICE_EXTENSION
KSPIN_LOCK spinlock> DEVICE_EXTENSION, * PDEVICE_EXTENSION;
*
NTSTATUS DriverEntry (.)
KelnitializeSpinLock (extensie-> spinlock);>
NTSTATUS DispatchReadWrite (.)
KIRQL Oldlrql;
KeAcquireSpinLock (extensie-> spinlock, 01dlrql); // proceseaza date, // protejate prin blocare cu spin
KeReleaseSpinLock (extensie-> spinlock, Oldlrql);>

Problema blocajelor

Dacă firul încearcă să reia spinlock-ul, acesta intră într-o buclă de așteptare infinită - "atârnă". Aceeași situație apare dacă două fire utilizează două încuietori. Fluxul 1 captează blocarea 1, în timp ce firul 2 captează blocarea 2. Streamul 1 încearcă apoi să capteze blocarea 2 și fluxul 2 - blocarea 1. Ambele fire "atârnă". Această situație poate fi extinsă la un număr arbitrar de fluxuri, este cunoscută și se numește impasuri.
Soluția la această problemă este foarte simplă. Toate încuietorile care pot fi capturate în același timp sunt plasate în listă în ordinea descrescătoare a frecvenței de utilizare. Dacă aveți nevoie să capturați încuietori, acestea trebuie să fie capturate în ordinea în care sunt enumerate. Astfel, am creat o ierarhie de încuietori.

Articole similare