• 40、总结IRP,handling IRPs,Part II


    you must understand the differences between a driver-created synchronous input/output request packet (IRP) and an asynchronous request. 

    Synchronous (Threaded) IRP

    Asynchronous (Non-Threaded) IRP

    Created by using

    IoBuildSynchronousFsdRequest or

    IoBuildDeviceIoControlRequest.

    Created by using IoBuildAsynchronousFsdRequest or

    IoAllocateIrp. This is meant for driver to driver

    communication.

    Thread must wait for the IRP to complete.

    Thread does not have to wait for the IRP to

    complete.

    Associated with the thread that created it, hence,

    the name threaded IRPs. Therefore, if the

    thread exits, the I/O manager cancels the IRP.

    Not associated with the thread that created it.

    Cannot be created in an arbitrary thread context.

    Can be created in an arbitrary thread context

    because the thread does not wait for the IRP to

    complete.

    The I/O manager does the post completions to

    free the buffer that is associated with the IRP.

    The I/O manager cannot do the cleanup. The

    driver must provide a completion routine and

    free the buffers that are associated with the IRP.

    Must be sent at IRQL level equal to

    PASSIVE_LEVEL.

    Can be sent at IRQL less than or equal to

    DISPATCH_LEVEL if the Dispatch routine of

    the target driver can handle the request at

    DISPATCH_LEVEL.

    同步IRP指创建IRP后,用IoCallDriver将这个IRP传递给底层驱动处理,一定等到底层驱动处理完毕后才会从IoCallDriver返回。

    异步IRP指创建IRP后,IoCallDriver会先返回,通过设置完成例程得到真正IRP的完成时间。

    1Send a synchronous device-control request (IRP_MJ_INTERNAL_DEVICE_CONTROL/IRP_MJ_DEVICE_CONTROL) by using IoBuildDeviceIoControlRequest

    代码
    NTSTATUS
    MakeSynchronousIoctl(
    IN PDEVICE_OBJECT TopOfDeviceStack,
    IN ULONG IoctlControlCode,
    PVOID InputBuffer,
    ULONG InputBufferLength,
    PVOID OutputBuffer,
    ULONG OutputBufferLength
    )
    /*++

    Arguments:

    TopOfDeviceStack-

    IoctlControlCode - Value of the IOCTL request

    InputBuffer - Buffer to be sent to the TopOfDeviceStack

    InputBufferLength - Size of buffer to be sent to the TopOfDeviceStack

    OutputBuffer - Buffer for received data from the TopOfDeviceStack

    OutputBufferLength - Size of receive buffer from the TopOfDeviceStack

    Return Value:

    NT status code

    --
    */
    {
    KEVENT
    event;
    PIRP irp;
    IO_STATUS_BLOCK ioStatus;
    NTSTATUS status;

    //
    // Creating Device control IRP and send it to the another
    // driver without setting a completion routine.
    //

    KeInitializeEvent(
    &event, NotificationEvent, FALSE);

    irp
    = IoBuildDeviceIoControlRequest (
    IoctlControlCode,
    TopOfDeviceStack,
    InputBuffer,
    InputBufferLength,
    OutputBuffer,
    OutputBufferLength,
    FALSE,
    // External
    &event,
    &ioStatus);

    if (NULL == irp) {
    return STATUS_INSUFFICIENT_RESOURCES;
    }


    status
    = IoCallDriver(TopOfDeviceStack, irp);

    if (status == STATUS_PENDING) {
    //
    // You must wait here for the IRP to be completed because:
    // 1) The IoBuildDeviceIoControlRequest associates the IRP with the
    // thread and if the thread exits for any reason, it would cause the IRP
    // to be canceled.
    // 2) The Event and IoStatus block memory is from the stack and we
    // cannot go out of scope.
    // This event will be signaled by the I/O manager when the
    // IRP is completed.
    //
    status = KeWaitForSingleObject(
    &event,
    Executive,
    // wait reason
    KernelMode, // To prevent stack from being paged out.
    FALSE, // You are not alertable
    NULL); // No time out !!!!

    status
    = ioStatus.Status;
    }

    return status;
    }

    2Send a synchronous device-control (IOCTL) request and cancel it if not completed in a certain time period

    This scenario is similar to the previous scenario except that instead of waiting indefinitely for the request to complete, it waits for some user-specified time and safely cancels the IOCTL request if the wait times out.

    代码
    typedef enum {

    IRPLOCK_CANCELABLE,
    IRPLOCK_CANCEL_STARTED,
    IRPLOCK_CANCEL_COMPLETE,
    IRPLOCK_COMPLETED

    } IRPLOCK;
    //
    // An IRPLOCK allows for safe cancellation. The idea is to protect the IRP
    // while the canceller is calling IoCancelIrp. This is done by wrapping the
    // call in InterlockedExchange(s). The roles are as follows:
    //
    // Initiator/completion: Cancelable --> IoCallDriver() --> Completed
    // Canceller: CancelStarted --> IoCancelIrp() --> CancelCompleted
    //
    // No cancellation:
    // Cancelable-->Completed
    //
    // Cancellation, IoCancelIrp returns before completion:
    // Cancelable --> CancelStarted --> CancelCompleted --> Completed
    //
    // Canceled after completion:
    // Cancelable --> Completed -> CancelStarted
    //
    // Cancellation, IRP completed during call to IoCancelIrp():
    // Cancelable --> CancelStarted -> Completed --> CancelCompleted
    //
    // The transition from CancelStarted to Completed tells the completer to block
    // postprocessing (IRP ownership is transferred to the canceller). Similarly,
    // the canceller learns it owns IRP postprocessing (free, completion, etc)
    // during a Completed->CancelCompleted transition.
    //


    NTSTATUS
    MakeSynchronousIoctlWithTimeOut(
    IN PDEVICE_OBJECT TopOfDeviceStack,
    IN ULONG IoctlControlCode,
    PVOID InputBuffer,
    ULONG InputBufferLength,
    PVOID OutputBuffer,
    ULONG OutputBufferLength,
    IN ULONG Milliseconds
    )
    /*++

    Arguments:

    TopOfDeviceStack -

    IoctlControlCode - Value of the IOCTL request.

    InputBuffer - Buffer to be sent to the TopOfDeviceStack.

    InputBufferLength - Size of buffer to be sent to the TopOfDeviceStack.

    OutputBuffer - Buffer for received data from the TopOfDeviceStack.

    OutputBufferLength - Size of receive buffer from the TopOfDeviceStack.

    Milliseconds - Timeout value in Milliseconds

    Return Value:

    NT status code

    --
    */
    {
    NTSTATUS status;
    PIRP irp;
    KEVENT
    event;
    IO_STATUS_BLOCK ioStatus;
    LARGE_INTEGER dueTime;
    IRPLOCK
    lock;

    KeInitializeEvent(
    &event, NotificationEvent, FALSE);

    irp
    = IoBuildDeviceIoControlRequest (
    IoctlControlCode,
    TopOfDeviceStack,
    InputBuffer,
    InputBufferLength,
    OutputBuffer,
    OutputBufferLength,
    FALSE,
    // External ioctl
    &event,
    &ioStatus);



    if (irp == NULL) {
    return STATUS_INSUFFICIENT_RESOURCES;
    }

    lock = IRPLOCK_CANCELABLE;

    IoSetCompletionRoutine(
    irp,
    MakeSynchronousIoctlWithTimeOutCompletion,
    &lock,
    TRUE,
    TRUE,
    TRUE
    );

    status
    = IoCallDriver(TopOfDeviceStack, irp);

    if (status == STATUS_PENDING) {

    dueTime.QuadPart
    = -10000 * Milliseconds;

    status
    = KeWaitForSingleObject(
    &event,
    Executive,
    KernelMode,
    FALSE,
    &dueTime
    );

    if (status == STATUS_TIMEOUT) {

    if (InterlockedExchange((PVOID)&lock, IRPLOCK_CANCEL_STARTED) == IRPLOCK_CANCELABLE) {

    //
    // You got it to the IRP before it was completed. You can cancel
    // the IRP without fear of losing it, because the completion routine
    // does not let go of the IRP until you allow it.
    //
    IoCancelIrp(irp);

    //
    // Release the completion routine. If it already got there,
    // then you need to complete it yourself. Otherwise, you got
    // through IoCancelIrp before the IRP completed entirely.
    //
    if (InterlockedExchange(&lock, IRPLOCK_CANCEL_COMPLETE) == IRPLOCK_COMPLETED) {
    IoCompleteRequest(irp, IO_NO_INCREMENT);
    }
    }

    KeWaitForSingleObject(
    &event, Executive, KernelMode, FALSE, NULL);

    ioStatus.Status
    = status; // Return STATUS_TIMEOUT

    }
    else {

    status
    = ioStatus.Status;
    }
    }

    return status;
    }

    NTSTATUS
    MakeSynchronousIoctlWithTimeOutCompletion(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    )
    {
    PLONG
    lock;

    lock = (PLONG) Context;

    if (InterlockedExchange((PVOID)&lock, IRPLOCK_COMPLETED) == IRPLOCK_CANCEL_STARTED) {
    //
    // Main line code has got the control of the IRP. It will
    // now take the responsibility of completing the IRP.
    // Therefore...
    return STATUS_MORE_PROCESSING_REQUIRED;
    }

    return STATUS_CONTINUE_COMPLETION ;
    }

    3Send a synchronous non-IOCTL request by using IoBuildSynchronousFsdRequest - completion routine returns STATUS_CONTINUE_COMPLETION

    代码
    NTSTATUS
    MakeSynchronousNonIoctlRequest (
    PDEVICE_OBJECT TopOfDeviceStack,
    PVOID WriteBuffer,
    ULONG NumBytes
    )
    /*++
    Arguments:

    TopOfDeviceStack -

    WriteBuffer - Buffer to be sent to the TopOfDeviceStack.

    NumBytes - Size of buffer to be sent to the TopOfDeviceStack.

    Return Value:

    NT status code


    --
    */
    {
    NTSTATUS status;
    PIRP irp;
    LARGE_INTEGER startingOffset;
    KEVENT
    event;
    IO_STATUS_BLOCK ioStatus;
    PVOID context;

    startingOffset.QuadPart
    = (LONGLONG) 0;
    //
    // Allocate memory for any context information to be passed
    // to the completion routine.
    //
    context = ExAllocatePoolWithTag(NonPagedPool, sizeof(ULONG), 'ITag');
    if(!context) {
    return STATUS_INSUFFICIENT_RESOURCES;
    }

    KeInitializeEvent(
    &event, NotificationEvent, FALSE);

    irp
    = IoBuildSynchronousFsdRequest(
    IRP_MJ_WRITE,
    TopOfDeviceStack,
    WriteBuffer,
    NumBytes,


    &startingOffset, // Optional
    &event,
    &ioStatus
    );

    if (NULL == irp) {
    ExFreePool(context);
    return STATUS_INSUFFICIENT_RESOURCES;
    }

    IoSetCompletionRoutine(irp,
    MakeSynchronousNonIoctlRequestCompletion,
    context,
    TRUE,
    TRUE,
    TRUE);

    status
    = IoCallDriver(TopOfDeviceStack, irp);

    if (status == STATUS_PENDING) {

    status
    = KeWaitForSingleObject(
    &event,
    Executive,
    KernelMode,
    FALSE,
    // Not alertable
    NULL);
    status
    = ioStatus.Status;
    }

    return status;
    }
    NTSTATUS
    MakeSynchronousNonIoctlRequestCompletion(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    )
    {
    if (Context) {
    ExFreePool(Context);
    }
    return STATUS_CONTINUE_COMPLETION ;

    }

    4Send a synchronous non-IOCTL request by using IoBuildSynchronousFsdRequest - completion routine returns STATUS_MORE_PROCESSING_REQUIRED

    代码
    NTSTATUS MakeSynchronousNonIoctlRequest2(
    PDEVICE_OBJECT TopOfDeviceStack,
    PVOID WriteBuffer,
    ULONG NumBytes
    )
    /*++ Arguments:
    TopOfDeviceStack

    WriteBuffer - Buffer to be sent to the TopOfDeviceStack.

    NumBytes - Size of buffer to be sent to the TopOfDeviceStack.

    Return Value:
    NT status code
    --
    */
    {
    NTSTATUS status;
    PIRP irp;
    LARGE_INTEGER startingOffset;
    KEVENT
    event;
    IO_STATUS_BLOCK ioStatus;
    BOOLEAN isSynchronous
    = TRUE;

    startingOffset.QuadPart
    = (LONGLONG) 0;
    KeInitializeEvent(
    &event, NotificationEvent, FALSE);
    irp
    = IoBuildSynchronousFsdRequest(
    IRP_MJ_WRITE,
    TopOfDeviceStack,
    WriteBuffer,
    NumBytes,
    &startingOffset, // Optional
    &event,
    &ioStatus
    );

    if (NULL == irp) {
    return STATUS_INSUFFICIENT_RESOURCES;
    }

    IoSetCompletionRoutine(irp,
    MakeSynchronousNonIoctlRequestCompletion2,
    (PVOID)
    &event,
    TRUE,
    TRUE,
    TRUE);

    status
    = IoCallDriver(TopOfDeviceStack, irp);

    if (status == STATUS_PENDING) {

    KeWaitForSingleObject(
    &event,
    Executive,
    KernelMode,
    FALSE,
    // Not alertable
    NULL);
    status
    = irp->IoStatus.Status;
    isSynchronous
    = FALSE;
    }

    //
    // Because you have stopped the completion of the IRP, you must
    // complete here and wait for it to be completed by waiting
    // on the same event again, which will be signaled by the I/O
    // manager.
    // NOTE: you cannot queue the IRP for
    // reuse by calling IoReuseIrp because it does not break the
    // association of this IRP with the current thread.
    //

    KeClearEvent(
    &event);
    IoCompleteRequest(irp, IO_NO_INCREMENT);

    //
    // We must wait here to prevent the event from going out of scope.
    // I/O manager will signal the event and copy the status to our
    // IoStatus block for synchronous IRPs only if the return status is not
    // an error. For asynchronous IRPs, the above mentioned copy operation
    // takes place regardless of the status value.
    //

    if (!(NT_ERROR(status) && isSynchronous)) {
    KeWaitForSingleObject(
    &event,
    Executive,
    KernelMode,
    FALSE,
    // Not alertable
    NULL);
    }
    return status;
    }

    NTSTATUS MakeSynchronousNonIoctlRequestCompletion2(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context )
    {
    if (Irp->PendingReturned) {
    KeSetEvent ((PKEVENT) Context, IO_NO_INCREMENT, FALSE);
    }
    return STATUS_MORE_PROCESSING_REQUIRED;
    }

    5Send an asynchronous request by using IoBuildAsynchronousFsdRequest

    In an asynchronous request, the thread that made the request does not have to wait for the IRP to complete. The IRP can be created in an arbitrary thread context because the IRP is not associated with the thread. You must provide a completion routine and release the buffers and IRP in the completion routine if you do not intend to reuse the IRP. This is because the I/O manager cannot do post-completion cleanup of driver-created asynchronous IRPs (created with IoBuildAsynchronousFsdRequest and IoAllocateIrp).

    代码
    NTSTATUS
    MakeAsynchronousRequest (
    PDEVICE_OBJECT TopOfDeviceStack,
    PVOID WriteBuffer,
    ULONG NumBytes
    )
    /*++
    Arguments:

    TopOfDeviceStack -

    WriteBuffer - Buffer to be sent to the TopOfDeviceStack.

    NumBytes - Size of buffer to be sent to the TopOfDeviceStack.

    --
    */
    {
    NTSTATUS status;
    PIRP irp;
    LARGE_INTEGER startingOffset;
    PIO_STACK_LOCATION nextStack;
    PVOID context;

    startingOffset.QuadPart
    = (LONGLONG) 0;

    irp
    = IoBuildAsynchronousFsdRequest(
    IRP_MJ_WRITE,
    TopOfDeviceStack,
    WriteBuffer,
    NumBytes,
    &startingOffset, // Optional
    NULL
    );

    if (NULL == irp) {

    return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // Allocate memory for context structure to be passed to the completion routine.
    //
    context = ExAllocatePoolWithTag(NonPagedPool, sizeof(ULONG_PTR), 'ITag');
    if (NULL == context) {
    IoFreeIrp(irp);
    return STATUS_INSUFFICIENT_RESOURCES;
    }

    IoSetCompletionRoutine(irp,
    MakeAsynchronousRequestCompletion,
    context,
    TRUE,
    TRUE,
    TRUE);
    //
    // If you want to change any value in the IRP stack, you must
    // first obtain the stack location by calling IoGetNextIrpStackLocation.
    // This is the location that is initialized by the IoBuildxxx requests and
    // is the one that the target device driver is going to view.
    //
    nextStack = IoGetNextIrpStackLocation(irp);
    //
    // Change the MajorFunction code to something appropriate.
    //
    nextStack->MajorFunction = IRP_MJ_SCSI;

    (
    void) IoCallDriver(TopOfDeviceStack, irp);

    return STATUS_SUCCESS;
    }
    NTSTATUS
    MakeAsynchronousRequestCompletion(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    )
    {
    PMDL mdl, nextMdl;

    //
    // If the target device object is set up to do buffered i/o
    // (TopOfDeviceStack->Flags and DO_BUFFERED_IO), then
    // IoBuildAsynchronousFsdRequest request allocates a system buffer
    // for read and write operation. If you stop the completion of the IRP
    // here, you must free that buffer.
    //

    if(Irp->AssociatedIrp.SystemBuffer && (Irp->Flags & IRP_DEALLOCATE_BUFFER) ) {
    ExFreePool(Irp
    ->AssociatedIrp.SystemBuffer);
    }

    //
    // If the target device object is set up do direct i/o (DO_DIRECT_IO), then
    // IoBuildAsynchronousFsdRequest creates an MDL to describe the buffer
    // and locks the pages. If you stop the completion of the IRP, you must unlock
    // the pages and free the MDL.
    //

    else if (Irp->MdlAddress != NULL) {
    for (mdl = Irp->MdlAddress; mdl != NULL; mdl = nextMdl) {
    nextMdl
    = mdl->Next;
    MmUnlockPages( mdl ); IoFreeMdl( mdl );
    // This function will also unmap pages.
    }
    Irp
    ->MdlAddress = NULL;
    }

    if(Context) {
    ExFreePool(Context);
    }



    //
    // If you intend to queue the IRP and reuse it for another request,
    // make sure you call IoReuseIrp(Irp, STATUS_SUCCESS) before you reuse.
    //
    IoFreeIrp(Irp);

    //
    // NOTE: this is the only status that you can return for driver-created asynchronous IRPs.
    //
    return STATUS_MORE_PROCESSING_REQUIRED;
    }

    6Send an asynchronous request by using IoAllocateIrp

    代码
    NTSTATUS
    MakeAsynchronousRequest2(
    PDEVICE_OBJECT TopOfDeviceStack,
    PVOID WriteBuffer,
    ULONG NumBytes
    )
    /*++
    Arguments:

    TopOfDeviceStack -

    WriteBuffer - Buffer to be sent to the TopOfDeviceStack.

    NumBytes - Size of buffer to be sent to the TopOfDeviceStack.

    --
    */
    {
    NTSTATUS status;
    PIRP irp;
    LARGE_INTEGER startingOffset;
    KEVENT
    event;
    PIO_STACK_LOCATION nextStack;

    startingOffset.QuadPart
    = (LONGLONG) 0;

    //
    // Start by allocating the IRP for this request. Do not charge quota
    // to the current process for this IRP.
    //

    irp
    = IoAllocateIrp( TopOfDeviceStack->StackSize, FALSE );
    if (NULL == irp) {

    return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // Obtain a pointer to the stack location of the first driver that will be
    // invoked. This is where the function codes and the parameters are set.
    //

    nextStack
    = IoGetNextIrpStackLocation( irp );
    nextStack
    ->MajorFunction = IRP_MJ_WRITE;
    nextStack
    ->Parameters.Write.Length = NumBytes;
    nextStack
    ->Parameters.Write.ByteOffset= startingOffset;


    if(TopOfDeviceStack->Flags & DO_BUFFERED_IO) {

    irp
    ->AssociatedIrp.SystemBuffer = WriteBuffer;
    irp
    ->MdlAddress = NULL;

    }
    else if (TopOfDeviceStack->Flags & DO_DIRECT_IO) {
    //
    // The target device supports direct I/O operations. Allocate
    // an MDL large enough to map the buffer and lock the pages into
    // memory.
    //
    irp->MdlAddress = IoAllocateMdl( WriteBuffer,
    NumBytes,
    FALSE,
    FALSE,
    (PIRP) NULL );
    if (irp->MdlAddress == NULL) {
    IoFreeIrp( irp );
    return STATUS_INSUFFICIENT_RESOURCES;
    }

    try {

    MmProbeAndLockPages( irp
    ->MdlAddress,
    KernelMode,
    (LOCK_OPERATION) (nextStack
    ->MajorFunction == IRP_MJ_WRITE ? IoReadAccess : IoWriteAccess) );

    } except(EXCEPTION_EXECUTE_HANDLER) {

    if (irp->MdlAddress != NULL) {
    IoFreeMdl( irp
    ->MdlAddress );
    }
    IoFreeIrp( irp );
    return GetExceptionCode();

    }
    }

    IoSetCompletionRoutine(irp,
    MakeAsynchronousRequestCompletion2,
    NULL,
    TRUE,
    TRUE,
    TRUE);

    (
    void) IoCallDriver(TargetDeviceObject, irp);

    return STATUS_SUCCESS;
    }

    NTSTATUS
    MakeAsynchronousRequestCompletion2(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    )
    {
    PMDL mdl, nextMdl;

    //
    // Free any associated MDL.
    //

    if (Irp->MdlAddress != NULL) {
    for (mdl = Irp->MdlAddress; mdl != NULL; mdl = nextMdl) {
    nextMdl
    = mdl->Next;
    MmUnlockPages( mdl ); IoFreeMdl( mdl );
    // This function will also unmap pages.
    }
    Irp
    ->MdlAddress = NULL;
    }

    //
    // If you intend to queue the IRP and reuse it for another request,
    // make sure you call IoReuseIrp(Irp, STATUS_SUCCESS) before you reuse.
    //

    IoFreeIrp(Irp);

    return STATUS_MORE_PROCESSING_REQUIRED;
    }

    See more about Send an asynchronous request and cancel it in a different thread in [4]

    参考

    [1] http://support.microsoft.com/kb/320275

    [2] Windows驱动开发技术详解,张帆

    [3] Walter Oney. Programming Windows Driver Model, Second Edition, Chapter 5.

    [4] http://support.microsoft.com/kb/326315/en-us/

    [5] http://support.microsoft.com/select/?target=hub

     

  • 相关阅读:
    弹出的iframe 表单无法获取焦点
    记录IE的两则BUG
    manifest 在移动端的应用实验
    [hover 状态图片变小] 淘宝的一个小用户体验
    使用 JSONP 的几点注意
    Apple在线商店iphone5, ipad mini到货提醒 邮件
    关于弹窗中的事件多次执行
    zen coding 插件使用实践
    hadoop系列整理Spark
    hadoop系列整理HBase
  • 原文地址:https://www.cnblogs.com/mydomain/p/1903710.html
Copyright © 2020-2023  润新知