zoukankan      html  css  js  c++  java
  • IoCopyCurrentIrpStackLocationToNext与IoSetCompletionRoutine的深入理解

    1、
    IoCopyCurrentIrpStackLocationToNext是拷贝本层的IO_STACK_LOCATION 到下一层。在楚狂人的驱动教程中说:
    如果对irp完成之后的事情有兴趣,并打算在完成函数中处理,应该首先拷贝当前 IO_STACK_LOCATION(IoCopyCurrentIrpStackLocationToNext),然后指定完成函数,并返回 IoCallDriver()所返回的status.完成函数中,不需要调用IoCompleteRequest!直接返回Irp的当前状态即可.但是IoCopyCurrentIrpStackLocationToNext是这样的:

    将本层的IO_STACK_LOCATION拷贝到下一层的方法一:

    #define IoCopyCurrentIrpStackLocationToNext( Irp ) {    
        PIO_STACK_LOCATION __irpSp;           
        PIO_STACK_LOCATION __nextIrpSp;       
        __irpSp = IoGetCurrentIrpStackLocation( (Irp) );        
        __nextIrpSp = IoGetNextIrpStackLocation( (Irp) );       
        RtlCopyMemory(__nextIrpSp, __irpSp, FIELD_OFFSET(IO_STACK_LOCATION, CompletionRoutine));      
        __nextIrpSp->Control = 0; }  

    也就是说他并没有拷贝当前的完成例程给下层,而是通过IoSetCompletionRoutine来设置的 这样一来的话,拷不拷贝本层的IO_STACK_LOCATION 到下层不都是没有关系的了?因为下层的IO_STACK_LOCATION 有他自己的结构内容,何必要用本层的拷贝过去呢?

    2、IoSetCompletionRoutine

    #define IoSetCompletionRoutine(irp,routine,completioncontext,success,error,cancel)
    #{ PIO_STACK_LOCATION irpsp;
    #ASSERT((success)|(error)|(cancel)?(routine)!=NULL:TRUE);
    #irpsp=IoGetNextIrpStackLocation((irp));
    #irpsp->completionroutine=(routine);
    #irpsp->context=(completioncontext);
    #irpsp->control=0;
    #if((success)){irpsp->control=SL_INVOKE_ON_SUCCESS;}
    #if((error)){irpsp->control  |= SL_INVOKE_ON_ERROR;}
    #if((cancel)){irpsp->control  |=  SL_INVOKE_ON_CANCEL;}  }

    这样一来IoSetCompletionRoutine不是设置的下层的完成例程么?为什么是设置下层的完成例程?为什么不是本层的?

    3、

    将本层的IO_STACK_LOCATION拷贝到下一层的方法二:

    PIO_STACK_LOCATION IrpSp;   
    PIO_STACK_LOCATION NextIrpSp;   
      
    IrpSp = IoGetCurrentIrpStackLocation(Irp);   
    NextIrpSp = IoGetNextIrpStackLocation(Irp);   
      
    *NextIrpSp = *IrpSp;  

    return IoCallDriver(NextDeviceObject, Irp);

    这种方法OSR中说IO_STACK_LOCATION中有两个成员CompletionRoutine、Context,即完成例程和完成例程的上下文参数。也就是说方法二会让Lb层拥有这两个原本不一定会属于它的成员。如果这两个成员都是NULL,那还好。一旦这两个成员有有效的内容,那么就会导致"an eventual blue screen"。但是在filemon源码的FilemonHookRoutine例程靠后中有

    *nextIrpStack = *currentIrpStack;

    但是随后

    #if defined(_IA64_)
            IoSetCompletionRoutine( Irp, FilemonHookDone, (PVOID) (ULONG_PTR) seqNum, TRUE, TRUE, TRUE );
    #else
            IoSetCompletionRoutine( Irp, FilemonHookDone, (PVOID) seqNum, TRUE, TRUE, TRUE );
    #endif

    而并没有出现什么蓝屏。而且我不明白为什么会出现蓝屏原本不属于他的成员CompletionRoutine、Context在IoSetCompletionRoutine不就是设置NextIrpSp 自己的成员么?(IoSetCompletionRoutine的定义  ) 

    答疑:

    1,完成例程本来就是设置在当前堆栈的下一层堆栈里,这相当于是一个规范,也可以用实际的IRP的返回来理解。在完成例程里,根据返回不同的状态值,IRP的控制流可能会发生相应的变化,比如:...STATUS_MORE_PROCESSING,这样,下层堆栈执行完成例程后,会将IRP的控制权交付给本层堆栈。从这个意义上讲,完成例程,只能放在下层堆栈,实际上,设计也是这样的。

    2,拷贝当前堆栈的内容到下层堆栈,只是为了保证执行环境一样。
    在一个设备栈中,高层设备只能访问自己的设备栈或者下层设备栈,这就要求这个驱动必须要为下层设置IO堆栈,但不是必须的。每个堆栈中,context字段的值是唯一的,会标识一些pending等状态位,表示不同的完成状态,所以这个字段不可以随意复制。 

     

       两种方法都挺常用。但是今天再看DDK中一篇OSR的分析文章里提到一个采用方法二可能会导致的一个很隐蔽的BUG:
       方法二把本层(La)的整个IO_STACK_LOCATION都拷贝到了下一层(Lb),而IO_STACK_LOCATION中有两个成员CompletionRoutine、Context,即完成例程和完成例程的上下文参数。也就是说方法二会让Lb层拥有这两个原本不一定会属于它的成员。如果这两个成员都是NULL,那还好。一旦这两个成员有有效的内容,那么就会导致"an eventual blue screen"。

    原来这是个宏,重点看RtlCopyMemory调用的最后一个参数:这个宏只拷贝了CompletionRoutine成员之前的部分。方法一、方法二的区别就在这里。而实际上后者相对于前者,并没有什么优点,所以还是尽量使用方法一比较好。

  • 相关阅读:
    leetcode 18 4Sum
    leetcode 71 Simplify Path
    leetcode 10 Regular Expression Matching
    leetcode 30 Substring with Concatenation of All Words
    leetcode 355 Design Twitte
    leetcode LRU Cache
    leetcode 3Sum
    leetcode Letter Combinations of a Phone Number
    leetcode Remove Nth Node From End of List
    leetcode Valid Parentheses
  • 原文地址:https://www.cnblogs.com/qintangtao/p/3437312.html
Copyright © 2011-2022 走看看