驅動程式 - Windows Driver Model (WDM) - 教學說明 - 5. File Buffer的使用策略



在AddDevice()中,當Create Device後,必須初始化相關旗標,其中有一個設定是關於I/O Manager的Buffer使用方式

static NTSTATUS AddDevice(PDRIVER_OBJECT pMyDriver, PDEVICE_OBJECT pPhyDevice)
{
    ...
    pMyDevice->Flags |= DO_BUFFERED_IO;
    ...
}

該Flags主要用於什麼地方呢?

當User Application透過檔案系統跟驅動程式溝通時,User Application必須將Buffer Pointer傳給Win32 API

int __cdecl main(int argc, char **argv)
{
    ...
    ReadFile(hFile, szBuffer, sizeof(szBuffer), &dwRet, NULL);
    WriteFile(hFile, szBuffer, sizeof(szBuffer), &dwRet, NULL);
    ...
}

szBuffer是User Buffer Pointer

當User Application呼叫ReadFile()、WriteFile()後,I/O Manager會呼叫驅動程式的IRP_MJ_READ、IRP_MJ_WRITE Callback副程式,那在驅動程式的Callback副程式要如何拿到User Application的Buffer資料呢?User Space的資料位址跟Kernel Space的資料位址是位於不同的地址空間,意思就是驅動程式不能直接存取這個User Buffer Pointer,所以I/O Manager要如何將這塊Buffer交給驅動程式,其使用的方式,就是所謂I/O Manager的Buffer使用策略,在Windows驅動程式中,I/O Manager有三種Buffer使用方式

使用方式行為描述
DO_BUFFERED_IOI/O Manager會新增一塊跟User Buffer一樣大小的記憶體,讀寫都需要由I/O Manager負責同步更新(進行複製的動作)
DO_DIRECT_IOI/O Manager會Mapping User Buffer到一個MDL(Memory Description List),驅動程式使用這個MDL進行操作,相較於DO_BUFFERED_IO,因為資料不須由I/O Manager做複製的動作,因此,效率會比較好
DO_NEITHER_IOMicrosoft沒有定義DO_NEITHER_IO這個旗標,而Neither I/O的意思是Neither Buffered Nor Direct I/O,因此,當沒有設定DO_BUFFERED_IO或DO_DIRECT_IO時,就會使用Neither的方式,使用這種方式時,I/O Manager會將Use Buffer Pointer,透過特殊操作後,再傳遞給驅動程式使用,因為,驅動程式可以直接存取User Buffer的資料,因此,效率是這三種方式裡面最好的一個

驅動程式必須依據Buffer的設定去存取不同欄位的Pointer,這樣才可以正確存取到User Buffer

使用方式IRPBuffer PointerBuffer Length
DO_BUFFERED_IOIRP_MJ_READ(IRP)
AssociatedIrp.SystemBuffer
(IrpStack)
Parameters.Read.Length
IRP_MJ_WRITE(IRP)
AssociatedIrp.SystemBuffer
(IrpStack)
Parameters.Write.Length
DO_DIRECT_IO(PIO)IRP_MJ_READ(MDL)
MmGetSystemAddressForMdlSafe
(MDL)
MmGetMdlByteCount
IRP_MJ_WRITE(MDL)
MmGetSystemAddressForMdlSafe
(MDL)
MmGetMdlByteCount
DO_DIRECT_IO(DMA)IRP_MJ_READ(MDL)
MmGetMdlVirtualAddress
(MDL)
MmGetMdlByteCount
IRP_MJ_WRITE(MDL)
MmGetMdlVirtualAddress
(MDL)
MmGetMdlByteCount
DO_NEITHER_IOIRP_MJ_READ(IRP)
UserBuffer
(IrpStack)
Parameters.Read.Length
IRP_MJ_WRITE(IRP)
UserBuffer
(IrpStack)
Parameters.Write.Length

DO_BUFFERED_IO的流程(IRP_MJ_READ)


DO_DIRECT_IO(PIO)的流程(IRP_MJ_READ)


DO_DIRECT_IO(DMA)的流程(IRP_MJ_READ)