剛看完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,
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(
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;
ZwReadFile(
device_extension->file_handle, //讀取指定的映像檔案
&irp->IoStatus,
buffer, //檔案緩沖
io_stack->Parameters.Read.Length,
&io_stack->Parameters.Read.ByteOffset,
RtlCopyMemory(system_buffer, buffer, io_stack->Parameters.Read.Length); //将檔案緩沖讀入到MDL緩沖
ExFreePool(buffer);//釋放檔案緩沖
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;
ZwWriteFile(
device_extension->file_handle,
MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority), //從MDL緩沖寫入檔案
io_stack->Parameters.Write.Length,
&io_stack->Parameters.Write.ByteOffset,
); //寫檔案
//下面的是自定義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未公開函數
case IOCTL_FILE_DISK_CLOSE_FILE:
irp->IoStatus.Status = FileDiskCloseFile(device_object, irp);
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
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已經處理
//傳回成功
return STATUS_SUCCESS;
//通知用戶端等IRP_MJ_READ/WRITE的響應
IoMarkIrpPending(Irp);
//插入到Irq處理隊列鍊頭
ExInterlockedInsertTailList(
&Irp->Tail.Overlay.ListEntry,
//觸發線程事件
KeSetEvent(
(KPRIORITY) 0,
FALSE
//傳回等待處理響應
return STATUS_PENDING;
從上面的代碼可以友善的看出FileDisk驅動的IRP序列化的實作過程,方法很簡單,将IRP包儲存在List_head連表中,同時把與之相對應的自反鎖儲存在list_lock當中,在利用request_event事件通知驅動處理IRP.