應用層與內核的幾種通信方式
應用程序與驅動程序據我所知,細分可以分6種,ReadFile,WirteFile方式的緩沖區設備讀寫,直接方式讀寫,和其他方式讀寫。Io設備控制操作(即DeviceControl)的緩沖內存模式IOCTL,直接內存方式的IOCTL,其他內存方式的IOCTL!當然還有一種就是創建文件,然後文件讀寫也應該算是一種通信吧,這裏不討論這個!
1,緩沖區方式設備讀寫:
在創建Device後,須要指定方式為Device的Flags有DO_BUFFERED_IO!通過應用層Api函數ReadFile,WriteFile,等函數,ntoskrnl.exe創建Irp後,ReadFile和WriteFile參數的緩沖區就在irp->AssociatedIrp.Systembuffer,同時要求讀寫的偏移量,和長度都在PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp) 數據類型中的stack->Parameters.Read.Length,stack->Parameters.Read.ByteOffset(該類型為Large_Interge類型),
2,直接方式讀寫
在創建Device後,須要指定方式為Device的Flags有DO_DIRECT_IO!通過應用層APi函數ReadFile,WriteFile等函數,ntoskrnl.exe創建的Irp後,ReadFile和WriteFile參數的緩沖區將被鎖住,然後操作系統將這段緩沖區在內核模式地址再次映射一遍,這樣應用層的緩沖區和內存層的就指向同一個物理內存!而內核模式用MDL數據結構記錄這段內存,這個虛擬內存大小在MmGetByteCount(pIrp->MdlAddress),首地址在MmGetMdlVirtualAddress(pIrp->MdlAddress);偏移量為MmGetMdlByteOffset(pIrp->MdlAddress)(這裏的偏移量不是文件讀寫的偏移量,而是在MDL中的偏移量)然後文件的長度還是stack->Parameters.Read.Length,這個值和MmGetByteCount(pIrp->MdlAddress)是一樣的,要不然就出錯了,而真正的讀寫偏移量還是在stack->Parameters.Read.ByteOffset!這裏無論是讀還是寫,都要得到MDL在內核模式下的映射,因此還要用MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority)將其轉化為內核模式,然後可以讀寫該地址,就會轉化到應用層相應的內存!!
3其他方式讀寫
這種方式很少用到,在創建Device後,Flags既不標誌DO_BUFFERED_IO也不標誌DO_DIRECT_IO,ReadFile和WriteFile提供的緩沖區內存地址,可以再IRP的pIrp->UserBuffer字段得到,而長度和偏移量還是在stack->Paameters.Read中,但是用這種方法須要註意的是ReadFile可能把空指針地址或者非法地址傳遞給驅動程序,因此驅動程序使用用戶模式地址錢須要檢查是否可讀或者可寫,可以用ProbeForWrite或者ProbeForWrite函數和try模塊。
這裏有個問題困擾著我,就是應用層用GetFileSize函數時用第一種方法處理該Irp時結果是正確的,就是沖pIrp->AssociatedIrp.SystemBuffer;得到PFILE_STANDARD_INFORMATION結構指針,然後講該結構的EndOfFile設置為你自己文件的長度,然後結束該IRP,GetFileSize就能得到正確的文件長度,但是用方法三就是的時候用GetFileSize,我同樣用pIrp->UserBufer得到PFILE_STANDARD_INFORMATION結構指針,然後設置後,在GetFileSize裏面去得不到正確的長度!!這個是為什麽?還有就是該Irp的CurrentStackLocation-stack,該字段裏面的stack->Parameters.QueryFile.Length指的是什麽,顯示的都是24,無論文長度為多少,還是用哪種方法都市24,很郁悶!!!
下面是方式都是用IO設備控制操作的方法基本上與上面3中相對應!
對於IO設備控制操作,不知道為什麽可以用設置DO_DIRECT_IO後就能對三種方式都適用,而且stack->Parameters.Read.Length/Write.Length 都是對應應用層的DeviceIoControl函數中的第6個參數也就是輸出緩沖區!DeviceIoControl函數的輸入輸出緩沖區長度都再stack->Parameters.DeviceioControl.InputBufferLength/OutputBufferLength中
4緩沖內存IOCTL,在DeviceIoControl函數第二個參數的時候,使用CTL_CODE來產生該常數,其中Method字段設置為METHOD_BUFFERED,在內核模式中輸入緩沖區很輸出緩沖區都為pIrp->AssociatedIrp.SystemBuffer,
5直接方式IOCTL,在DeviceIoControl函數第二個參數的時候,使用CTL_CODE來產生該常數,其中Method字段設置為METHOD_IN_DIRECT/METHOD_OUT_DIRECT(最好使用第一個,因為第一個對所有的打開設備方式都通用)!對於第4中的區別是輸入緩沖區還在pIrp->AssocatedIrp.Systembuffer中,但是輸出緩沖區卻是pIrp->MdlAddress,因此在內核對輸出的寫應該寫在MmGetSystemAddressForMdlSafe(pIrp->MdlAddress)中,
6其他方式IOCTL,在DeviceIoControl函數第二個參數的時候,使用CTL_CODE來產生該常數,其中Method字段設置為MEHTOD_NEITHER,輸入緩沖區為stack->Parameters.DeviceIoControl.Tyep3InputBuffer;輸出緩沖區為pIrp->Userbuffer
應用層與內核的幾種通信方式