天天看點

WDF隊列分析(1)--序幕

    WDF架構中IoQueue的實作相當複雜,覆寫的内容很廣,需要大量的篇幅來分析。至于demo程式,還是以我們熟悉的toaster入手。雖然IoQueue的實作分散在WDF源碼的很多角落,但我還是整理出一個線索,這幾篇文章會圍繞着它逐漸展開。好,現在來看下所謂的線索:

kd> g
Breakpoint 0 hit
wdfsimple!ToasterEvtIoRead+0x39:
a64b41b9 8b5508          mov     edx,dword ptr [ebp+8]
kd> kb
ChildEBP RetAddr  Args to Child              
acd13a00 86715c84 368477c8 4fbd0228 0000000a wdfsimple!ToasterEvtIoRead+0x39 [c:\winddk\7600.16385.1\src\general\toaster\kmdf\func\simple\toaster.c @ 306]
acd13a1c 8674c82a 368477c8 4fbd0228 0000000a Wdf01000!FxIoQueueIoStop::Invoke+0x2e [minkernel\wdf\framework\shared\inc\private\common\fxioqueuecallbacks.hpp @ 159]
acd13a54 86712b91 0000000a b042fdd0 c97b8830 Wdf01000!FxIoQueue::DispatchRequestToDriver+0x39a0a [minkernel\wdf\framework\shared\irphandlers\io\fxioqueue.cpp @ 3272]
acd13a78 867133df acd13a00 00000000 af9b88f0 Wdf01000!FxIoQueue::DispatchEvents+0x241 [minkernel\wdf\framework\shared\irphandlers\io\fxioqueue.cpp @ 3125]
acd13a98 86711bb6 b042fdd0 8ac98244 8d656020 Wdf01000!FxIoQueue::QueueRequest+0x7f [minkernel\wdf\framework\shared\irphandlers\io\fxioqueue.cpp @ 2371]
acd13af0 821584b8 01656020 00647374 c96472e0 Wdf01000!FxDevice::DispatchWithLock+0x316 [minkernel\wdf\framework\shared\core\fxdevice.cpp @ 1430]
acd13b0c 8239ce2c c9647398 c96472e0 8d656020 nt!IofCallDriver+0x48      

這是toaster處理IRP_MJ_READ時的調用堆棧,請不要因為他是所謂的線索而感到意外,我也是順着這個調用棧,慢慢梳理出IoQueue的輪廓。是以,請你耐心的往下看下去。

    如果熟悉wdm驅動,那你一定知道調用IoCallDriver後,會調用對應驅動的Dispatch函數。根據上面堆棧的最後兩行輸出,可知Toaster處理IRP_MJ_READ的Dispatch函數是FxDevice::DispatchWithLock。是不是有點震驚?看toaster\kmdf\func\simple\toaster.c的代碼,MS像是把Dispatch函數設定為:

WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig,  WdfIoQueueDispatchParallel);

    queueConfig.EvtIoRead = ToasterEvtIoRead;
    queueConfig.EvtIoWrite = ToasterEvtIoWrite;
    queueConfig.EvtIoDeviceControl = ToasterEvtIoDeviceControl;      

    看來一定是FxDevice::DispatchWithLock封裝并調用了ToasterEvtIoRead。是以現在要做的是确定IRP_MJ_READ回調函數确實是FxDevice::DispatchWithLock(具體步驟請​​移步我的另一篇部落格​​):

kd> !drvobj 8e54baf8 7
Driver object (8e54baf8) is for:
 \Driver\wdffeatured
Driver Extension List: (id , addr)
(862ecd8a a4a06068)  
Device Object list:
b0a90390  

DriverEntry:   a82224e0 wdffeatured!FxDriverEntry
DriverStartIo: 00000000 
DriverUnload:  a82225cc wdffeatured!FxStubDriverUnload
AddDevice:     862b09de Wdf01000!FxDriver::AddDevice

Dispatch routines:
[00] IRP_MJ_CREATE                      862918a0  Wdf01000!FxDevice::DispatchWithLock
[02] IRP_MJ_CLOSE                       862918a0  Wdf01000!FxDevice::DispatchWithLock
[03] IRP_MJ_READ                        862918a0  Wdf01000!FxDevice::DispatchWithLock
[04] IRP_MJ_WRITE                       862918a0  Wdf01000!FxDevice::DispatchWithLock
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL     862918a0  Wdf01000!FxDevice::DispatchWithLock
[10] IRP_MJ_SHUTDOWN                    862918a0  Wdf01000!FxDevice::DispatchWithLock
[12] IRP_MJ_CLEANUP                     862918a0  Wdf01000!FxDevice::DispatchWithLock
[16] IRP_MJ_POWER                       862918a0  Wdf01000!FxDevice::DispatchWithLock
[17] IRP_MJ_SYSTEM_CONTROL              862918a0  Wdf01000!FxDevice::DispatchWithLock
[1b] IRP_MJ_PNP                         862918a0  Wdf01000!FxDevice::DispatchWithLock      

參windbg的輸出所示,WDF驅動的IRP處理函數清一色被設定成FxDevice::DispatchWithLock!起初我還以為調試器出了問題,反複核對源碼才确定這既是事實!已知IRP處理函數是FxDevice::DispatchWithLock,隻要查找它的所有引用即可知道在哪設定了這些派遣函數。但是,先不要急着查找引用,反問自己一個問題:在wdm中,一般什麼時候設定驅動派遣函數?答案是在DriverEntry傳回前。以此類推,wdf驅動也做了同樣的事。在wdf中DriverEntry主要是調用WdfCreateDriver,是以,我猜測為驅動程式設定回調函數是由WdfCreateDriver完成。我們可以在FxDriver::Initialize中找到這樣的代碼:

WdfDriverCreate(...)
{
...
    pDriver = new(pFxDriverGlobals, DriverAttributes)
        FxDriver(DriverObject, DriverConfig, pFxDriverGlobals);

    if (pDriver != NULL) {

        if (NT_SUCCESS(status)) {

            status = pDriver->Initialize(RegistryPath, DriverConfig, DriverAttributes);
...
}      

在FxDriver::Initialize中,驅動程式的派遣函數在循環内被統一的設定成Wdf01000!FxDevice::DispatchWithLock,如下:

FxDriver::Initialize(
__in PCUNICODE_STRING ArgRegistryPath,
__in PWDF_DRIVER_CONFIG Config,
__in_opt PWDF_OBJECT_ATTRIBUTES DriverAttributes
)
{
    #if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE))
    for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
            if (FxDevice::_RequiresRemLock(i, 0x0) == FxDeviceRemLockNotRequired) {
                m_DriverObject.SetMajorFunction(i, FxDevice::Dispatch);
            }
            else {
                m_DriverObject.SetMajorFunction(i, FxDevice::DispatchWithLock);
            }
}      

嗯,感覺DispatchWithLock十有八九是個代理函數:根據不同類型的IRP,做不同的處理。再繼續分析之前,容我啰嗦一下DispatchWithLock這個函數名中的Lock的含義:它指裝置對象擴充域中的自定義的RemoveLock。在WDF架構中,處理PNP/Power這類IRP前需要擷取RemoveLock,而處理Create/Close/Cleanup以及Read/Write/ioctl這類IRP沒有強制需要獲得RemoveLock。關于RemoveLock的細節,參考win7 ddk的event樣例。

    回到正題,DispatchWithLock在判斷和擷取RemoveLock後,先後進入FxDevice::Dispatch和DispatchWorker。DispatchWorker将引出本篇的主角----IoQueue:

__inline
NTSTATUS
DispatchWorker(
    __in FxDevice*  Device,
    __in MdIrp       Irp,
    __in WDFCONTEXT DispatchContext
    )
{
    ...
    return Device->GetDispatchPackage(
        irp.GetMajorFunction()
        )->Dispatch(Irp);
}      

GetDispatchPackage傳回FxPackage*基類指針,Dispatch是類FxPackage中的虛函數。很明顯,MS是想通過多态實作對IRP多元化處理。目前WDF中有4種FxPackage的派生類滿足4種處理方式:

1).IRP_MJ_Create/Close/Cleanup/Shutdown對應的派生類為FxPkgGeneral;

2).IRP_MJ_Read/Write/DeviceIoControl對應的派生類為FxPkgIo;

繼續閱讀