刚看完FileDisk代码,感受颇多,其中采用线程的方式进行IRP序列化更是让人觉得新鲜.在DDK中一般采用StartIo来进行IRP的序列话,其中在入口函数中加入pDriverObject->DriverStartUp = XXXStartIO即可, 当然也没这么简单,其中还要做写处理.
FileDisk采用了另外一种方法,先来看看代码;
//为IRP消息而设的双向循环链表结构
InitializeListHead(&device_extension->list_head);
//自旋锁初始化,锁住双向链表,在线程处理中解锁
KeInitializeSpinLock(&device_extension->list_lock);
//设置同步事件
KeInitializeEvent(
&device_extension->request_event,
SynchronizationEvent,//同步事件操作,在每一次wait通过后自动将事件设为未受信状态,
//使下一次wait阻塞.NotificationEvent则不会自动设置,需要手动
FALSE//初始状态为未受信状态
);
//设置线程有效
device_extension->terminate_thread = FALSE;
//建立主线程,实际上一个盘只有一个线程操作
status = PsCreateSystemThread(
&thread_handle,//获得线程句柄
(ACCESS_MASK) 0L,//屏蔽消息
NULL,
NULL,
NULL,
FileDiskThread,//触发磁盘操作线程
device_object
);
这里可以看出,将FileDisk将IRP包保存在List_head当中,方便于在FileDiskThread线程直接进行处理.接下来看看FileDiskThread线程代码如何实现;
VOID
FileDiskThread (
IN PVOID Context
)
{
PDEVICE_OBJECT device_object;
PDEVICE_EXTENSION device_extension;
PLIST_ENTRY request;//双向链表的IRP栈
PIRP irp;
PIO_STACK_LOCATION io_stack;
PUCHAR system_buffer;
PUCHAR buffer;
ASSERT(Context != NULL);//?
device_object = (PDEVICE_OBJECT) Context;
device_extension = (PDEVICE_EXTENSION) device_object->DeviceExtension;
//设置优先级
KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY);
for (;;)//以下为线程的无限循环,当device_extension->terminate_thread设为true时才结束.
{
//等待request_event事件置信,for每循环一次,在这里被阻塞,等待
KeWaitForSingleObject(
&device_extension->request_event,
Executive,
KernelMode,
FALSE,
NULL
);
//如果结束线程标志置位,结束线程
if (device_extension->terminate_thread)
{
PsTerminateSystemThread(STATUS_SUCCESS);
}
//遍历IRP表
while (request = ExInterlockedRemoveHeadList(
&device_extension->list_head,
&device_extension->list_lock
)) //list不为空,则指向下一链表数据
{
irp = CONTAINING_RECORD(request, IRP, Tail.Overlay.ListEntry); //获取irp基地址,tail是IRP结构里面的一个Union结构,Tail.Overlay.ListEntry说明IRP入栈待处理
////IRP的栈返回形式
io_stack = IoGetCurrentIrpStackLocation(irp);
switch (io_stack->MajorFunction)
{
case IRP_MJ_READ://读数据到内存
system_buffer = (PUCHAR) MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority);//分配MDL虚拟地址空间
if (system_buffer == NULL)
{
irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;//分配失败返回资源不足警告
irp->IoStatus.Information = 0;
break;
}
buffer = (PUCHAR) ExAllocatePool(PagedPool, io_stack->Parameters.Read.Length); //分配空间
if (buffer == NULL)
{
irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
irp->IoStatus.Information = 0;
break;
}
ZwReadFile(
device_extension->file_handle, //读取指定的映像文件
NULL,
NULL,
NULL,
&irp->IoStatus,
buffer, //文件缓冲
io_stack->Parameters.Read.Length,
&io_stack->Parameters.Read.ByteOffset,
NULL
);
RtlCopyMemory(system_buffer, buffer, io_stack->Parameters.Read.Length); //将文件缓冲读入到MDL缓冲
ExFreePool(buffer);//释放文件缓冲
break;
case IRP_MJ_WRITE://内存数据写到设备
if ((io_stack->Parameters.Write.ByteOffset.QuadPart +
io_stack->Parameters.Write.Length) >
device_extension->file_size.QuadPart)//判错处理
{
irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
irp->IoStatus.Information = 0;
}
ZwWriteFile(
device_extension->file_handle,
NULL,
NULL,
NULL,
&irp->IoStatus,
MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority), //从MDL缓冲写入文件
io_stack->Parameters.Write.Length,
&io_stack->Parameters.Write.ByteOffset,
NULL
); //写文件
break;
//下面的是自定义IRP响应
case IRP_MJ_DEVICE_CONTROL:
switch (io_stack->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_FILE_DISK_OPEN_FILE:
//nt未公开函数,应该是权限设置
SeImpersonateClient(device_extension->security_client_context, NULL);
irp->IoStatus.Status = FileDiskOpenFile(device_object, irp);
PsRevertToSelf();//nt未公开函数
break;
case IOCTL_FILE_DISK_CLOSE_FILE:
irp->IoStatus.Status = FileDiskCloseFile(device_object, irp);
break;
default:
irp->IoStatus.Status = STATUS_DRIVER_INTERNAL_ERROR;
}
break;
default:
irp->IoStatus.Status = STATUS_DRIVER_INTERNAL_ERROR;
}
IoCompleteRequest(
irp,
(CCHAR) (NT_SUCCESS(irp->IoStatus.Status) ?
IO_DISK_INCREMENT : IO_NO_INCREMENT)
);
}
}
}
不得不看看WRITE和READ IRP是怎么处理的,代码如下;
NTSTATUS
FileDiskReadWrite (//完成对虚拟磁盘的读写,对应于主功能代码IRP_MJ_WRITE和IRP_MJ_READ,
//在进行读写之前要先确认设备是否存在。
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PDEVICE_EXTENSION device_extension;
PIO_STACK_LOCATION io_stack;
device_extension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
if (!device_extension->media_in_device)//虚拟盘内没介质则出错
{
Irp->IoStatus.Status = STATUS_NO_MEDIA_IN_DEVICE;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_NO_MEDIA_IN_DEVICE;
}
io_stack = IoGetCurrentIrpStackLocation(Irp);
if (io_stack->Parameters.Read.Length == 0)//如果要求读写长度为0,直接返回
{
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
//标识IRP已经处理
IoCompleteRequest(Irp, IO_NO_INCREMENT);
//返回成功
return STATUS_SUCCESS;
}
//通知客户端等IRP_MJ_READ/WRITE的响应
IoMarkIrpPending(Irp);
//插入到Irq处理队列链头
ExInterlockedInsertTailList(
&device_extension->list_head,
&Irp->Tail.Overlay.ListEntry,
&device_extension->list_lock
);
//触发线程事件
KeSetEvent(
&device_extension->request_event,
(KPRIORITY) 0,
FALSE
);
//返回等待处理响应
return STATUS_PENDING;
}
从上面的代码可以方便的看出FileDisk驱动的IRP序列化的实现过程,方法很简单,将IRP包保存在List_head连表中,同时把与之相对应的自反锁保存在list_lock当中,在利用request_event事件通知驱动处理IRP.