[原创]浅谈那些年来我们用过的设备操作

驱动所创建的设备一般有三种操作方式:缓冲区方式DO_BUFFERED_IO,直接方式DO_DIRECT_IO,其他方式
1.缓冲区
缓冲区地址属于内核地址,因为要是在用户层的话,会导致进程的切换问题最终发生严重的错误。
(1.)利用缓冲区操作必须先在底层申请一块内存地址来存放这些数据;
(2.)我们关心的是如何得到写入的大小和数据,File_Size=stack->Parameters.Read.Length;
.....
详细请看代码:[IRP_MJ_READ]
NTSTATUS Writea(IN PDEVICE_OBJECT pDevice,
IN PIRP pIrp)
{
NTSTATUS Status=STATUS_SUCCESS;
PDEVICE_EXTENSION PDeviN=(PDEVICE_EXTENSION)pDevice->DeviceExtension;
PIO_STACK_LOCATION stack=IoGetCurrentIrpStackLocation(pIrp);
ULONG WriteLenth=stack->Parameters.Write.Length;
ULONG WriteOffset=stack->Parameters.Write.ByteOffset.QuadPart;
if (WriteLenth+WriteOffset>1024) //1024是我们分配的内存大小,超过了你很可能就挂彩了
{
Status=STATUS_FILE_INVALID;
WriteLenth=0;
}
else
{
KdPrint(("write buffer 0x%x\n",PDeviN->buffer));
KdPrint(("write Ox%x\n",WriteOffset));
KdPrint(("write Ox%x\n",WriteLenth));
memcpy(PDeviN->buffer+WriteOffset,pIrp->AssociatedIrp.SystemBuffer,WriteLenth);
Status=STATUS_SUCCESS;
if (WriteLenth+WriteOffset>PDeviN->file_length)
{
PDeviN->file_length=WriteLenth+WriteOffset;
}
}
pIrp->IoStatus.Status=Status;
pIrp->IoStatus.Information=WriteLenth;
IoCompleteRequest(pIrp,IO_NO_INCREMENT);
return Status;
}(3.)
NTSTATUS Reada(IN PDEVICE_OBJECT pDevObj,
IN PIRP pIrp)
{
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
ULONG ReadLength = stack->Parameters.Read.Length;
ULONG ReadOffset = (ULONG)stack->Parameters.Read.ByteOffset.QuadPart;
if (ReadOffset+ReadLength>1024)
{
status = STATUS_FILE_INVALID;
ReadLength = 0;
}else
{
KdPrint(("Read buffer 0x%x\n",pDevExt->buffer));
KdPrint(("Read WriteOffset Ox%x\n",ReadOffset));
KdPrint(("Read WriteLenth Ox%x\n",ReadLength));
memcpy(pIrp->AssociatedIrp.SystemBuffer,pDevExt->buffer+ReadOffset,ReadLength);
status = STATUS_SUCCESS;
}
pIrp->IoStatus.Status=status;
pIrp->IoStatus.Information=ReadLength;
IoCompleteRequest(pIrp,IO_NO_INCREMENT);
return status;
}2.直接设备操作DO_DIRECT_IO
(1.)在于此时的flags已经不是DO_BUFFERED_IO而是DO_DIRECT_IO
(2.)区别在于内核不需要在分配内存作为r3存储数据,这是用用户层的地址被锁定操作系统用(MDL)内存描述符来记录这段地址。
(3.)这段被MDL记录的地址包晗了 文件读写请求大小(Mdl_lenth) 相对偏移地址(Mdl_offset)
第一页地址(StartVa).那么这段虚拟内存地址应该就是Startva+Mdl_offset。
DDK中已经有宏来方便得到这些数值:
#define MmGetMdlVirtualAddress(Mdl) \
((PVOID) ((PCHAR) ((Mdl)->StartVa) + (Mdl)->ByteOffset))
//++
//
// ULONG
// MmGetMdlByteCount (
// __in PMDL Mdl
// )
//
// Routine Description:
//
// The MmGetMdlByteCount returns the length in bytes of the buffer
// described by the Mdl.
//
// Arguments:
//
// Mdl - Pointer to an MDL.
//
// Return Value:
//
// Returns the byte count of the buffer described by the Mdl
//
//--
#define MmGetMdlByteCount(Mdl) ((Mdl)->ByteCount)
//++
//
// ULONG
// MmGetMdlByteOffset (
// __in PMDL Mdl
// )
//
// Routine Description:
//
// The MmGetMdlByteOffset returns the byte offset within the page
// of the buffer described by the Mdl.
//
// Arguments:
//
// Mdl - Pointer to an MDL.
//
// Return Value:
//
// Returns the byte offset within the page of the buffer described by the Mdl
//
//--
#define MmGetMdlByteOffset(Mdl) ((Mdl)->ByteOffset)
//++
//
// PVOID
// MmGetMdlStartVa (
// __in PMDL Mdl
// )
//
// Routine Description:
//
// The MmGetMdlBaseVa returns the virtual address of the buffer
// described by the Mdl rounded down to the nearest page.
//
// Arguments:
//
// Mdl - Pointer to an MDL.
//
// Return Value:
//
// Returns the returns the starting virtual address of the MDL.
//
//
//--
#define MmGetMdlBaseVa(Mdl) ((Mdl)->StartVa)(4.)kernel_Address(映射后的内核地址,用来写数据和读数据用的)
通过
PVOID Kernel_Address=MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority);来实现
(5.)具体代码请看下面:
NTSTATUS Writea(IN PDEVICE_OBJECT pDevice,
IN PIRP pIrp)
{
KdPrint(("WIRTE ROUNTION"));
NTSTATUS Status=STATUS_SUCCESS;
PDEVICE_EXTENSION PDeviN=(PDEVICE_EXTENSION)pDevice->DeviceExtension;
PIO_STACK_LOCATION stack=IoGetCurrentIrpStackLocation(pIrp);
ULONG ulWriteLeng=stack->Parameters.Write.Length;
ULONG Mdl_offset=MmGetMdlByteOffset(pIrp->MdlAddress);
PVOID Mdl_address=MmGetMdlVirtualAddress(pIrp->MdlAddress);
ULONG Mdl_lenth=MmGetMdlByteCount(pIrp->MdlAddress);
if (Mdl_lenth!=ulWriteLeng)
{
Status=STATUS_UNSUCCESSFUL;
pIrp->IoStatus.Information=0;
}
else
{
KdPrint(("write buffer 0x%x\n",PDeviN->buffer));
KdPrint(("write Ox%x\n",Mdl_offset));
KdPrint(("write Ox%x\n",Mdl_lenth));
PVOID Kernel_Address=MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority);
Status=STATUS_SUCCESS;
pIrp->IoStatus.Information=Mdl_lenth;
}
pIrp->IoStatus.Status=Status;
IoCompleteRequest(pIrp,IO_NO_INCREMENT);
return Status;
}NTSTATUS Reada(IN PDEVICE_OBJECT pDevObj,
IN PIRP pIrp)
{
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
ULONG ulReadLength = stack->Parameters.Read.Length;
ULONG Mdl_lenth=MmGetMdlByteCount(pIrp->MdlAddress);
PVOID Mdl_Address=MmGetMdlVirtualAddress(pIrp->MdlAddress);
ULONG Mdl_offset=MmGetMdlByteOffset(pIrp->MdlAddress);
if (Mdl_lenth!=ulReadLength)
{
pIrp->IoStatus.Information=0;
status=STATUS_UNSUCCESSFUL;
}
else
{
PVOID Kernel_Address=MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority);
KdPrint(("the kernel address is 0x%x\n",Kernel_Address));
pIrp->IoStatus.Information=ulReadLength;
}
pIrp->IoStatus.Status=status;
IoCompleteRequest(pIrp,IO_NO_INCREMENT);
return status;
}
3.()
下面是R3代码:
#include <windows.h>
#include <stdio.h>
int main()
{
HANDLE hDevice =
CreateFile("\\\\.\\KANXUE",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
if (hDevice == INVALID_HANDLE_VALUE)
{
return 1;
}
UCHAR buffer[10];
memset(buffer,0xCC,8);
UCHAR F[10];
ULONG ulRead;
ULONG ulWrite;
BOOL bRet;
bRet = WriteFile(hDevice,buffer,8,&ulWrite,NULL);
if (bRet)
{
printf("写入了 %d 字节\n",ulWrite);
}
bRet = ReadFile(hDevice,F,8,&ulRead,NULL);
if (bRet)
{
printf("读出 %d 字节:",ulRead);
for (int i=0;i<(int)ulRead;i++)
{
//printf("%02X ",buffer[i]);
}
printf("%s ",F);
printf("\n");
}
getchar();
CloseHandle(hDevice);
return 0;
}效果图
