• 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成员之前的部分。方法一、方法二的区别就在这里。而实际上后者相对于前者,并没有什么优点,所以还是尽量使用方法一比较好。

  • 相关阅读:
    redis 秒杀设计,常用命令
    wamp下配置多域名和访问路径的方法
    git commit 因执行yarn , npm,报错推送不了
    taro bug--小程序报错Error: 未找到入口 sitemap.json 文件,或者文件读取失败,请检查后重新编译。
    vscode引用相对路径,scss时路径报错问题解决
    space-between与space-around的区别
    Android可设置任意高度的TextView,如:设置0.5px设置0.1px等等
    Android可以打开微信支付,但是没法调起小程序支付
    dask
    Joblib-lightweight piplining tool
  • 原文地址:https://www.cnblogs.com/qintangtao/p/3437312.html
Copyright © 2020-2023  润新知