Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Kendi IRP kuyruklarını yöneten sürücüler, kuyruklara erişimi senkronize etmek için sistem iptal spin kilidi yerine sürücü tarafından sağlanan bir spin kilidi kullanabilir. Kesinlikle gerekli olduğu durumlar dışında iptal döndürme kilidinin kullanımından kaçınarak performansı geliştirebilirsiniz. Sistemde yalnızca bir iptal döndürme kilidi olduğundan, bir sürücünün bazen bu döndürme kilidinin kullanılabilir olmasını beklemesi gerekebilir. Sürücü tarafından sağlanan bir döndürme kilidinin kullanılması bu olası gecikmeyi ortadan kaldırır ve iptal döndürme kilidini G/Ç yöneticisi ve diğer sürücüler için kullanılabilir hale getirir. Sistem, sürücünün İptal yordamını çağırdığında iptal döndürme kilidini almaya devam etse de, bir sürücü IRP kuyruğunu korumak için kendi döndürme kilidini kullanabilir.
Sürücü bekleyen IRP'leri kuyruğa almasa ancak sahipliği başka bir şekilde korusa bile, bu sürücünün IRP için bir İptal yordamı ayarlaması ve IRP işaretçisini korumak için bir döndürme kilidi kullanması gerekir. Örneğin, bir sürücünün bekleyen bir IRP'yi işaretlediği ve ardından IRP işaretçisini bir IoTimer yordamına bağlam olarak geçirdiği varsayın. Sürücünün zamanlayıcıyı iptal eden bir İptal yordamı ayarlaması ve IRP'ye erişirken hem İptal yordamında hem de zamanlayıcı geri çağırmasında aynı döndürme kilidini kullanması gerekir.
Kendi IP'lerini kuyruğa alan ve kendi döndürme kilidini kullanan herhangi bir sürücü aşağıdakileri yapmalıdır:
Kuyruğu korumak için bir döndürme kilidi oluşturun.
Yalnızca bu spin kilidini tutarken İptal rutinini ayarlayın ve temizleyin.
Sürücü bir IRP'yi sıralarken cancel yordamı çalışmaya başlarsa, Cancel yordamının IRP'yi tamamlamasına izin verin.
cancel yordamında kuyruğu koruyan kilidi alın.
Döndürme kilidini oluşturmak için sürücü KeInitializeSpinLock'u çağırır. Aşağıdaki örnekte sürücü, oluşturduğu kuyrukla birlikte döndürme kilidini DEVICE_CONTEXT bir yapıya kaydeder:
typedef struct {
LIST_ENTRYirpQueue;
KSPIN_LOCK irpQueueSpinLock;
...
} DEVICE_CONTEXT;
VOID InitDeviceContext(DEVICE_CONTEXT *deviceContext)
{
InitializeListHead(&deviceContext->irpQueue);
KeInitializeSpinLock(&deviceContext->irpQueueSpinLock);
}
Bir IRP'yi kuyruğa almak için sürücü döndürme kilidini alır, InsertTailList'i çağırır ve aşağıdaki örnekte olduğu gibi IRP'yi beklemede olarak işaretler:
NTSTATUS QueueIrp(DEVICE_CONTEXT *deviceContext, PIRP Irp)
{
PDRIVER_CANCEL oldCancelRoutine;
KIRQL oldIrql;
NTSTATUS status;
KeAcquireSpinLock(&deviceContext->irpQueueSpinLock, &oldIrql);
// Queue the IRP and call IoMarkIrpPending to indicate
// that the IRP may complete on a different thread.
// N.B. It is okay to call these inside the spin lock
// because they are macros, not functions.
IoMarkIrpPending(Irp);
InsertTailList(&deviceContext->irpQueue, &Irp->Tail.Overlay.ListEntry);
// Must set a Cancel routine before checking the Cancel flag.
oldCancelRoutine = IoSetCancelRoutine(Irp, IrpCancelRoutine);
ASSERT(oldCancelRoutine == NULL);
if (Irp->Cancel) {
// The IRP was canceled. Check whether our cancel routine was called.
oldCancelRoutine = IoSetCancelRoutine(Irp, NULL);
if (oldCancelRoutine) {
// The cancel routine was NOT called.
// So dequeue the IRP now and complete it after releasing the spin lock.
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
// Drop the lock before completing the request.
KeReleaseSpinLock(&deviceContext->irpQueueSpinLock, oldIrql);
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_PENDING;
} else {
// The Cancel routine WAS called.
// As soon as we drop our spin lock, it will dequeue and complete the IRP.
// So leave the IRP in the queue and otherwise do not touch it.
// Return pending since we are not completing the IRP here.
}
}
KeReleaseSpinLock(&deviceContext->irpQueueSpinLock, oldIrql);
// Because the driver called IoMarkIrpPending while it held the IRP,
// it must return STATUS_PENDING from its dispatch routine.
return STATUS_PENDING;
}
Örnekte gösterildiği gibi, sürücü dönüş kilidini tutarken Cancel yordamını ayarlar ve temizler. Örnek kuyruğa alma yordamı IoSetCancelRoutine'e iki çağrı içerir.
İlk çağrı, IRP için Cancel yordamını ayarlar. Ancak, kuyruğa alma yordamı çalışırken IRP iptal edilmiş olabileceğinden, sürücünün IRP'nin İptal üyesini denetlemesi gerekir.
İptal ayarlanırsa iptal istenir ve sürücü daha önce ayarlanan İptal yordamının çağrılıp çağrılmadığını görmek için IoSetCancelRoutine'a ikinci bir çağrı yapmalıdır.
IRP iptal edildiyse ancak Cancel yordamı henüz çağrılmadıysa, geçerli yordam IRP'yi sıralar ve STATUS_CANCELLED ile tamamlar.
IRP iptal edildiyse ve Cancel yordamı zaten çağrıldıysa, mevcut dönüş IRP'yi bekliyor olarak işaretler ve STATUS_PENDING değerini döndürür. İptal rutini IRP'yi tamamlar.
Aşağıdaki örnekte, daha önce oluşturulan kuyruktan bir IRP'nin nasıl kaldırılacağı gösterilmektedir:
PIRP DequeueIrp(DEVICE_CONTEXT *deviceContext)
{
KIRQL oldIrql;
PIRP nextIrp = NULL;
KeAcquireSpinLock(&deviceContext->irpQueueSpinLock, &oldIrql);
while (!nextIrp && !IsListEmpty(&deviceContext->irpQueue)) {
PDRIVER_CANCEL oldCancelRoutine;
PLIST_ENTRY listEntry = RemoveHeadList(&deviceContext->irpQueue);
// Get the next IRP off the queue.
nextIrp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
// Clear the IRP's cancel routine.
oldCancelRoutine = IoSetCancelRoutine(nextIrp, NULL);
// IoCancelIrp() could have just been called on this IRP. What interests us
// is not whether IoCancelIrp() was called (nextIrp->Cancel flag set), but
// whether IoCancelIrp() called (or is about to call) our Cancel routine.
// For that, check the result of the test-and-set macro IoSetCancelRoutine.
if (oldCancelRoutine) {
// Cancel routine not called for this IRP. Return this IRP.
ASSERT(oldCancelRoutine == IrpCancelRoutine);
} else {
// This IRP was just canceled and the cancel routine was (or will be)
// called. The Cancel routine will complete this IRP as soon as we
// drop the spin lock, so do not do anything with the IRP.
// Also, the Cancel routine will try to dequeue the IRP, so make
// the IRP's ListEntry point to itself.
ASSERT(nextIrp->Cancel);
InitializeListHead(&nextIrp->Tail.Overlay.ListEntry);
nextIrp = NULL;
}
}
KeReleaseSpinLock(&deviceContext->irpQueueSpinLock, oldIrql);
return nextIrp;
}
Örnekte, sürücü kuyruğa erişmeden önce ilişkili döndürme kilidini alır. Döndürme kilidini tutarken kuyruğun boş olmadığını kontrol eder ve kuyruktan sıradaki IRP'yi çıkarır. Ardından IRP için İptal yordamını sıfırlamak üzere IoSetCancelRoutine çağrısında bulunur. Sürücü IRP'yi kuyruğundan çıkarırken ve Cancel rutinini sıfırlarken IRP iptal edilebileceği için, sürücünün IoSetCancelRoutine tarafından döndürülen değeri kontrol etmesi gerekir. IoSetCancelRoutinenull döndürürse ve bu da İptal yordamının olduğunu veya yakında çağrılacağını gösterirse, sıralamayı kaldırma yordamı İptal yordamının IRP'yi tamamlamasına olanak tanır. Ardından kuyruğu koruyan kilidi serbest bırakır ve geri döner.
Önceki yordamda InitializeListHead kullanımına dikkat edin. Sürücü IRP'yi yeniden kuyruğa alabilir, böylece İptal yordamı onu kuyruktan çıkarabilir, ancak IRP'nin ListEntry alanını IRP'nin kendisini işaret edecek şekilde yeniden başlatan InitializeListHead çağrısını yapmak daha basittir. İptal yordamı dönüş kilidini almadan önce listenin yapısı değişebileceği için, kendi kendine başvuran işaretçinin kullanımı önemlidir. Liste yapısı değişirse ve listEntry'nin özgün değeri geçersiz olursa, İptal yordamı IRP'yi sıraladığında listeyi bozabilir. Ancak ListEntry , IRP'nin kendisini işaret ederse İptal yordamı her zaman doğru IRP'yi kullanır.
İptal yordamına karşılık aşağıdakileri yapmanız yeterlidir:
VOID IrpCancelRoutine(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
DEVICE_CONTEXT *deviceContext = DeviceObject->DeviceExtension;
KIRQL oldIrql;
// Release the global cancel spin lock.
// Do this while not holding any other spin locks so that we exit at the right IRQL.
IoReleaseCancelSpinLock(Irp->CancelIrql);
// Dequeue and complete the IRP.
// The enqueue and dequeue functions synchronize properly so that if this cancel routine is called,
// the dequeue is safe and only the cancel routine will complete the IRP. Hold the spin lock for the IRP
// queue while we do this.
KeAcquireSpinLock(&deviceContext->irpQueueSpinLock, &oldIrql);
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
KeReleaseSpinLock(&deviceContext->irpQueueSpinLock, oldIrql);
// Complete the IRP. This is a call outside the driver, so all spin locks must be released by this point.
Irp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return;
}
G/Ç yöneticisi her zaman bir İptal yordamı çağırmadan önce genel iptal döndürme kilidini alır, bu nedenle İptal yordamının ilk görevi bu döndürme kilidini serbest bırakmaktır. Ardından, sürücünün IRP kuyruğunu koruyan döndürme kilidini alır, geçerli IRP'yi kuyruktan kaldırır, döndürme kilidini serbest bırakır, IRP'yi STATUS_CANCELLED ile ve öncelik artırma olmadan tamamlar ve geri döner.
Döndürme kilitlerini iptal etme hakkında daha fazla bilgi için Windows Sürücülerinde İptal Mantığı teknik incelemesine bakın.