Очереди IRP (пример из DDK)

Тема в разделе "WASM.NT.KERNEL", создана пользователем buktak, 28 окт 2007.

  1. buktak

    buktak New Member

    Публикаций:
    0
    Регистрация:
    2 апр 2007
    Сообщения:
    84
    Вот пример из DDK добавления IRP в очередь:
    Код (Text):
    1. NTSTATUS QueueIrp(DEVICE_CONTEXT *deviceContext, PIRP Irp)
    2. {
    3.     PDRIVER_CANCEL  oldCancelRoutine;
    4.     KIRQL  oldIrql;
    5.     NTSTATUS status = STATUS_PENDING;
    6.  
    7. KeAcquireSpinLock(&deviceContext->irpQueueSpinLock, &oldIrql);
    8.  
    9. //  Queue the IRP and call IoMarkIrpPending
    10. //  to indicate that the IRP may complete on a different thread.
    11. //  N.B. It's okay to call these inside the spin lock because they're macros, not functions.
    12. IoMarkIrpPending(Irp);
    13. InsertTailList(&deviceContext->irpQueue, &Irp->Tail.Overlay.ListEntry);
    14.  
    15. //  Must set a Cancel routine before checking the Cancel flag.
    16. oldCancelRoutine = IoSetCancelRoutine(Irp, IrpCancelRoutine);
    17. ASSERT(!oldCancelRoutine);
    18.  
    19. if (Irp->Cancel){
    20.     //  The IRP was canceled.  Check whether our cancel routine was called.
    21.     oldCancelRoutine = IoSetCancelRoutine(Irp, NULL);
    22.     if (oldCancelRoutine){
    23.         //  The cancel routine was NOT called.  
    24.         //  So dequeue the IRP now and complete it after releasing the spin lock.
    25.         RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
    26.         status = Irp->IoStatus.Status = STATUS_CANCELLED;
    27.     } //endif
    28.     else {
    29.         //  The cancel routine WAS called.  
    30.         //  As soon as we drop our spin lock it will dequeue and complete the IRP.
    31.         //  So leave the IRP in the queue and otherwise don't touch it.
    32.         //  Return pending since we're not completing the IRP here.
    33.     }  //end else
    34. } // endif
    35.  
    36. KeReleaseSpinLock(&deviceContext->irpQueueSpinLock, oldIrql);
    37.  
    38. // Normally you shouldn't call IoMarkIrpPending
    39. // and return a status other than STATUS_PENDING.
    40. // But you can break this rule if you complete the IRP.
    41. if (status != STATUS_PENDING){
    42.     IoCompleteRequest(Irp, IO_NO_INCREMENT);
    43. }
    44. return status;
    45. }
    Непонятно вот что:
    Код (Text):
    1. //  Must set a Cancel routine before checking the Cancel flag.
    Почему нельзя сразу проверить флаг Irp->Cancel? И что отвечает за присвоение Irp->Cancel = TRUE, это делается автоматически или это необходимо явно прописать в CancelRoutine.
     
  2. buktak

    buktak New Member

    Публикаций:
    0
    Регистрация:
    2 апр 2007
    Сообщения:
    84
    Ответ: это делает IoCancelIrp(Irp).
     
  3. buktak

    buktak New Member

    Публикаций:
    0
    Регистрация:
    2 апр 2007
    Сообщения:
    84
    Вот код с моими комментариями, может кто скажет, где я ошибаюсь.
    Код (Text):
    1. NTSTATUS QueueIrp(DEVICE_CONTEXT *deviceContext, PIRP Irp)
    2. {
    3.     PDRIVER_CANCEL  oldCancelRoutine;
    4.     KIRQL  oldIrql;
    5.     NTSTATUS status = STATUS_PENDING;
    6.  
    7. // устанавливаем спин-блокировку
    8. KeAcquireSpinLock(&deviceContext->irpQueueSpinLock, &oldIrql);
    9.  
    10. //указываем, что irp будет обработан позже
    11. IoMarkIrpPending(Irp);
    12. //добавляем в список irp
    13. InsertTailList(&deviceContext->irpQueue, &Irp->Tail.Overlay.ListEntry);
    14.  
    15. //устанавливаем CancelRoutine для irp. ВАЖНО: я так понял, CancelRoutine для этого irp устанавливается в первый раз.
    16. oldCancelRoutine = IoSetCancelRoutine(Irp, IrpCancelRoutine);
    17. ASSERT(!oldCancelRoutine);
    18.  
    19. //а вдруг до вызова QueueIrp(...) user-mode программа попыталась отменить свой запрос. Из кернел-мода был вызван IoCancelIrp(Irp), который вернул FALSE. Т.е. присвоил Irp->Cancel=TRUE, а CancelRoutine запустить не смог, ибо его установили только парой строчек выше.
    20. if (Irp->Cancel){
    21.     // Зачем нужна переменная oldCancelRoutine, я не знаю. Ибо в ней 100% будет назодится адрес IrpCancelRoutine.
    22.     oldCancelRoutine = IoSetCancelRoutine(Irp, NULL);
    23.     if (oldCancelRoutine){
    24.         //  Будет выполняться только этот код.
    25.         RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
    26.         status = Irp->IoStatus.Status = STATUS_CANCELLED;
    27.     } //endif
    28.     else {
    29.         //  Здесь ничего выполняться не будет.
    30.     }  //end else
    31. } // endif
    32.  
    33. KeReleaseSpinLock(&deviceContext->irpQueueSpinLock, oldIrql);
    34. }
     
  4. rain

    rain New Member

    Публикаций:
    0
    Регистрация:
    22 апр 2006
    Сообщения:
    976
    Установка рутины отмены и непосредственно отмена самого irp это разные вещи и не выполняются одновременно, это далается в разных частях кода в соответствии с алго. Если мутно объяснил то проще, кажется, так: Irp->Cancel означает что Irp _уже_ отменён, а Irp->CancelRoutine просто устанавливает рутину отмены.
     
  5. buktak

    buktak New Member

    Публикаций:
    0
    Регистрация:
    2 апр 2007
    Сообщения:
    84
    А вот и не значит :) Это значит, что irp попытались удалить с помощью IoCancelIrp.
     
  6. Four-F

    Four-F New Member

    Публикаций:
    0
    Регистрация:
    31 авг 2002
    Сообщения:
    1.237
    Рекомендую прочитать в 5-ой главе книги Walter Oney - "Programming The Windows Driver Model 2nd Edition" разделы "Queuing I/O Requests" и "Cancelling I/O Requests". Там подробно отвечено на все вопросы.

    Есть переводное издание http://www.piter.com/book/978591180057/