3.2.1 访问数据的I/O方式:
1. 缓冲方式I/O
2. 直接方式I/O
3. 非缓冲非直接方式I/O
3.2.2 读写驱动程序
读写驱动程序,即应用程序或者上层驱动程序发送主功能码为IRP_MJ_READ和IRP_MJ_WRITE的I/O请求包(IRP)
//
//驱动入口函数
//
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
UNICODE_STRING DeviceName,Win32Device;
PDEVICE_OBJECT DeviceObject = NULL;
NTSTATUS status;
unsigned i;
KdPrint(("[DriverEntry]\n"));
//设备名
RtlInitUnicodeString(&DeviceName,L"\\Device\\Demo0");
//符号链接号
RtlInitUnicodeString(&Win32Device,L"\\DosDevice\\Demo0");
//填写默认的IRP处理函数
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
{
DriverObject->MajorFunction[i] = DemoDefaultHandler;
}
//IRP处理函数
DriverObject->MajorFunction[IRP_MJ_CREATE] = DemoCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DemoCreateClose;
DriverObject->MajorFunction[IRP_MJ_READ] = DemoReadWrite;
DriverObject->MajorFunction[IRP_MJ_WRITE] = DemoReadWrite;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DemoDevControl;
//驱动卸载函数
DriverObject->DriverUnload = DemoUnload;
//创建设备
status = IoCreateDevice(DriverObject, 10, &DeviceName, FILE_DEVICE_UNKNOWN,
0, FALSE, &DeviceObject);
if (!NT_SUCCESS(status))
{
return status;
}
if (!DeviceObject)
{
return STATUS_UNEXPECTED_IO_ERROR;
}
//初始化这个字节的内容
memset(DeviceObject->DeviceExtension, 'A', 10);
//设置设备的读写方式
//DeviceObject->Flags |= DO_BUFFERED_IO;
//DeviceObject->Flags |= DO_DIRECT_IO;
//创建符号链接
status = IoCreateSymbolicLink(&Win32Device, &DeviceName);
//设备初始化完毕,可以开始工作了
DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
return STATUS_SUCCESS;
}
//
//读写请求
//写请求的处理只是简单地向设备自定义扩展中复制数据
//读请假的处理只是简单地从设置自定义扩展中读取数据
//注意,最大支持10字节
//
NTSTATUS DemoReadWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION pSP = IoGetCurrentIrpStackLocation(Irp);
PVOID pBuffer = NULL;
BOOLEAN bNeither = FALSE;
ULONG uLen = 0;
KdPrint(("[DemoReadWrite]\n"));
if (DeviceObject->Flags & DO_BUFFERED_IO)
{
KdPrint(("Flags: DO_BUFFER_IO\n"));
pBuffer = Irp->AssociatedIrp.SystemBuffer;
}
else if (DeviceObject->Flags & DO_DIRECT_IO)
{
KdPrint(("Flag: DO_DRIECT_IO\n"));
pBuffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
}
else
{
KdPrint(("Flags: Neither\n"));
bNeither = TRUE;
pBuffer = Irp->UserBuffer;
}
switch (pSP->MajorFunction)
{
case IRP_MJ_READ:
uLen = pSP->Parameters.Read.Length;
uLen = uLen>10?10:uLen;
KdPrint(("IRP_MJ_READ Read Len: %d\n", pSP->Parameters.Read.Length));
if (FALSE == bNeither)
{
RtlCopyMemory(pBuffer, DeviceObject->DeviceExtension, uLen);
}
else
{
//两者皆不需要验证缓冲区的有效性
_try
{
ProbeForWrite(pBuffer,uLen,4);
RtlCopyMemory(pBuffer,DeviceObject->DeviceExtension,uLen);
}
_except(EXCEPTION_EXECUTE_HANDLER)
{
KdPrint(("IRP_MJ_READ exception!\n"));
status = STATUS_UNSUCCESSFUL;
}
}
break;
case IRP_MJ_WRITE:
uLen = pSP->Parameters.Write.Length;
uLen = uLen>10?10:uLen;
KdPrint(("IRP_MJ_WRITE WRITE Len: %d\n",pSP->Parameters.Write.Length));
if (FALSE == bNeither)
{
RtlCopyMemory(DeviceObject->DeviceExtension,pBuffer,uLen);
}
else
{
_try
{
ProbeForRead(pBuffer,uLen,4);
RtlCopyMemory(DeviceObject->DeviceExtension,pBuffer,uLen);
}
_except(EXCEPTION_EXECUTE_HANDLER)
{
KdPrint(("IRP_MJ_WRITE exception!\n"));
status = STATUS_UNSUCCESSFUL;
}
}
break;
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = uLen;
IoCompleteRequest(Irp,IO_NO_INCREMENT);
return status;
}
3.2.3 发送I/O控制码
应用程序或者上层的驱动程序可以通过发送I/O控制码和目标设备驱动程序交互,由驱动程序的IRP(主功能码为IRP_MJ_DEVICE_CONTROL)处理函数提供支持处理
I/O控制码的布局包含:设备类型(Device Type)、请求权限(RequiredAccess)、功能码(FunctionCode)、传输类型(TransferType)
设备类型:必须和打开设备的设备类型相匹配,设备类型包括FILE_DEVICE_DISK、FILE_DEVICE_UNKNOWN等
请求权限:包括FILE_ANY_ACCESS、FILE_READ_DATA和FILE_WRITE_DATA
功能码:用于区分不同的控制权限,所有小于0x800的功能保留给微软、0x800以及更大值的功能码提供给供应商使用
传输类型:指定了不同的I/O访问方式:METHOD_BUFFERED、METHOD_IN_DIRCET、METHOD_OUT_DIRECT或者METHOD_NEITHER
I/O控制码可以使用系统提供的CTL_CODE宏定义,宏如下:
#define IOCTL_DEVICE_Function CTL_CODE(DeviceType, Function, Method, Access)
//
//define
//
#define IOCTL_BUFFERED_IO\
CTL_CODE(FILE_DEVICE_UNKNOWN,0x8000,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define IOCTL_INDIRECT_IO\
CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_IN_DIRECT,FILE_ANY_ACCESS)
#define IOCTL_NEITHER_IO\
CTL_CODE(FILE_DEVICE_UNKNOWN,0x802,METHOD_NEITHER,FILE_ANY_ACCESS)
//
//自定义控制请求
//同样也是在应用程序和设备扩展之间复制数据
//数据最多为10字节
//
NTSTATUS DemoDevControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION pSP = IoGetCurrentIrpStackLocation(Irp);
ULONG uControlCode = pSP->Parameters.DeviceIoControl.IoControlCode;
PVOID pInBuf = NULL, pOutBuf = NULL;
ULONG uInLen = 0, uOutLen = 0;
KdPrint(("[DemoDevControl]\n"));
uInLen = pSP->Parameters.DeviceIoControl.InputBufferLength;
uInLen = uInLen>10?10:uOutLen;
uOutLen = pSP->Parameters.DeviceIoControl.OutputBufferLength;
uOutLen = uOutLen>10?10:uOutLen;
KdPrint(("uInLen: %d uOutLen: %d\n",
pSP->Parameters.DeviceIoControl.InputBufferLength,
pSP->Parameters.DeviceIoControl.OutputBufferLength));
switch(uControlCode)
{
case IOCTL_BUFFERED_IO:
KdPrint(("IOCTL_BUFFER_IO\n"));
pInBuf = pOutBuf = Irp->AssociatedIrp.SystemBuffer;
if (uInLen)
{
RtlCopyMemory(DeviceObject->DeviceExtension,pInBuf,uInLen);
}
if (uOutLen)
{
RtlCopyMemory(pOutBuf,DeviceObject->DeviceExtension,uOutLen);
}
break;
case IOCTL_INDIRECT_IO:
KdPrint(("IOCTL_INDIRECT_IO\n"));
pInBuf = Irp->AssociatedIrp.SystemBuffer;
pOutBuf = MmGetSystemAddressForMdl(Irp->MdlAddress);
if (uInLen)
{
RtlCopyMemory(DeviceObject->DeviceExtension,pInBuf,uInLen);
}
if (uOutLen)
{
RtlCopyMemory(pOutBuf,DeviceObject->DeviceExtension,uOutLen);
}
break;
case IOCTL_NEITHER_IO:
KdPrint(("IOCTL_NEITHER_IO\n"));
pInBuf = pSP->Parameters.DeviceIoControl.Type3InputBuffer;
pOutBuf = Irp->UserBuffer;
_try
{
if (uInLen)
{
ProbeForRead(pInBuf,uInLen,4);
RtlCopyMemory(DeviceObject->DeviceExtension,pInBuf,uInLen);
}
if (uOutLen)
{
ProbeForWrite(pOutBuf,uOutLen,4);
RtlCopyMemory(pOutBuf,DeviceObject->DeviceExtension,uOutLen);
}
}
_except(EXCEPTION_EXECUTE_HANDLER)
{
KdPrint(("exception!\n"));
status = STATUS_UNSUCCESSFUL;
}
break;
default:
status = STATUS_UNSUCCESSFUL;
break;
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = uOutLen;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
3.2.4 内存共享
应用程序和驱动程序可以共享内存。共享内存有两种实现方式:应用程序分配内存,提供给驱动程序,由驱动程序映射并锁定该内存;驱动程序分配内存,然后映射到应用程序地址范围内。
后一种方式:
1. 驱动程序分配一块内核空间
2. 使用MDL描述这片内存并锁定内存,映射到用户空间
3. 将映射到用户空间的地址提交给应用程序,此后,应用程序即可使用该地址操作这片共享内存了
#define IOCTL_SHARE_MEMORY\
CTL_CODE(FILE_DEVICE_UNKNOWN,0x803,IOCTL_SHARE_MEMORY,FILE_ANY_ACCESS)
case IOCTL_SHARE_MEMORY:
pOutBuf = Irp->AssociatedIrp.SystemBuffer;
//将设备扩展作为共享内存地址,大小为10字节
pSysAddr = DeviceObject->DeviceExtension;
pMdl = IoAllocateMdl(pSysAddr,10,FALSE,FALSE,NULL)
if (NULL == pMdl)
{
KdPrint(("IOCTL_SHARE_MEMORY IoAllocateMdl Failure!\n"));
status = STATUS_UNSUCCESSFUL;
break;
}
MmBuildMdlForNonPagedPool(pMdl);
*(ULONG*)pOutBuf = MmMapLockedPagesSpecifyCache(
pMdl, UserMode, MmNonCached, NULL, FALSE, NormalPagePriority);
if (NULL == *(ULONG)*pOutBuf)
{
KdPrint(("IOCTL_SHARE_MEMORY MmMaoLockedPagesSpecifyCache Failure\n"));
status = STATUS_UNSUCCESSFUL;
IoFreeMdl(pMdl);
break;
}
KdPrint(("IOCTL_SHARE_MEMORY SysBuf:0x%08x UserBuf:0x%08x\n",pSysAddr,*(ULONG*)pOutBuf));
break;