天天看點

FileDisk 線程方式進行IRP序列化

      剛看完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.

繼續閱讀