應用層與驅動層同步事件處理方法
關於這個問題高手略過吧。
Ring3與Ring0同步是很有用的手段,在此做一個簡要的整理,希望對開發這方面程序的朋友有幫助,好了,開始吧。
1 同步的策略
初寫驅動的朋友都知道,通過DeviceIoControl這個API函數, 可以將Ring3的數據及控制請求發送給Ring0.但這是單向的,由Ring3主動發起,Ring0被動接受。可以說這是單向輪詢的。
詢問(CTL_CODE)
Ring3 ---------------------------------------------------------------- > Ring0
回答(通過OUT參數)
Ring3 <---------------------------------------------------------------- Ring0
這種方式用於主動獲取Ring0數據,又不要求效率,和具有實時性的開發情況。
但做監控類軟件卻與此相反,是Ring0先監控到事件,具有主動權,發起詢問通知Ring3。
監控到事件(通知)
Ring0(監控)----------------------------------------------------------------> Ring3
在實時性要求不高的情況下可以採用Ring3定時詢問Ring0,當然Ring0保存了事件的狀態。
這裏介紹的就是高實時性,大數據傳輸的同步解決辦法。
原理:通過Ring3創建事件,並將該事件傳遞給Ring0,同時Ring3創建監控線程,等待Ring0發起事件,此爲應用層驅動層共享事件。同時Ring0在內核分配非分頁內存,通過DeviceIoControl 傳遞給Ring3,此爲應用層驅動層共享內存。因在DeviceIoControl 中傳送Buffer,涉及到內核數據拷貝,大數據量下使用效率很低,故用共享內存的方法。
2 具體實現
Ring3 CODE :
HANDLE m_hEvent = CreateEvent(NULL,FALSE,FALSE,NULL); // 創建事件
// 發事件給ring 0
if (0 == DeviceIoControl(hFile, \
SET_EVENT, \
&m_hEvent, \
sizeof(HANDLE), \
NULL, \
0,\
&uRetBytes,
NULL))
{
CloseHandle(hFile);
CloseHandle(m_hEvent);
return ;
}
// 獲得ring 0 的共享內存
unsigned int add = 0;
if (0 == DeviceIoControl(hFile, \
GET_SHARE_ADD, \
NULL, \
0, \
&add, \
sizeof (unsigned int),\
&uRetBytes,
NULL))
{
CloseHandle(hFile);
CloseHandle(m_hEvent);
return ;
}
m_pShareMem =(PVOID)add; //映射的共享內存
當Ring0有事件產生時,Ring3的線程就可處理m_pShareMen了。
UINT WorkThread(PVOID param)
{ ...............
while(!bExit)
{
WaitForSingleObject(g_hEvent,INFINITE);
......................
// 可以處理分析m_pShareMen 了
}
}
Ring0 CODE :
PVOID g_pSysAdd = NULL;
PMDL g_pMdl = NULL; // 與ring 3 的共享內存描述
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryString)
{
......
g_pSysAdd = ExAllocatePool(NonPagedPool, SHARE_MLEN + IPHEDLEN);
g_pMdl = IoAllocateMdl(g_pSysAdd, SHARE_MLEN + IPHEDLEN, FALSE, FALSE, NULL);
MmBuildMdlForNonPagedPool(g_pMdl);
......
}
// I/O控制派遣例程
NTSTATUS DispatchIoctl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
// 取得I/O控制代碼
ULONG uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
switch(uIoControlCode)
{
case SET_EVENT: // 處理Ring3的事件
if(g_pInBuffer == NULL||g_nInBuffSize < sizeof(HANDLE))
{
status = STATUS_INVALID_BUFFER_SIZE;
break;
}
hEvent = *(HANDLE *)g_pInBuffer;
status = ObReferenceObjectByHandle(hEvent,
SYNCHRONIZE,
*ExEventObjectType,
KernelMode,
(PVOID *)&g_pEvent,
&objHandleInfo
);
if(!NT_SUCCESS(status))
{
g_pEvent = NULL;
}
DbgPrint("g_pEvent: %x\n",g_pEvent); // 得到了ring 3 事件句柄
break;
case GET_SHARE_ADD: // 傳給ring 3 內存地址
UserAddr = MmMapLockedPages(g_pMdl, UserMode);
*((PVOID *)(pIrp->AssociatedIrp.SystemBuffer)) = UserAddr;
status = STATUS_SUCCESS;
break;
}
..............
}
RtlCopyMemory(g_pSysAdd,pSource,dwLen) // 將寫數據到共享內存,
KeSetEvent((PKEVENT)g_pEvent,0,false); // 此刻Ring3就可以接受數據了。
最後一步記得釋放內存。
void DriverUnload(PDRIVER_OBJECT pDriverObj)
{
// 清除共享內存
IoFreeMdl(g_pMdl);
ExFreePool(g_pSysAdd);
// 刪除設備對象
IoDeleteDevice(pDriverObj->DeviceObject);
}
以上就是整個處理過程。