[普通]内核层与应用层通信详解

作者(passion) 阅读(1184次) 评论(0) 分类( 内核开发)

做驱动开发的肯定会遇到应用层与内核层的通信的问题,首先说内核层与应用层的通信可以大概分为两个方面,第一是应用层向内核层主动传递消息,第二是内核层主动与应用层通信。下面我们将分开来谈两个方面。

我们先来看应用层向内核层传递的方法:


[cpp] view plain copy

  1. BOOL DeviceIoControl (   

  2. HANDLE hDevice, // 设备句柄   

  3. DWORD dwIoControlCode, // IOCTL请求操作代码   

  4. LPVOID lpInBuffer, // 输入缓冲区地址   

  5. DWORD nInBufferSize, // 输入缓冲区大小   

  6. LPVOID lpOutBuffer, // 输出缓冲区地址   

  7. DWORD nOutBufferSize, // 输出缓冲区大小   

  8. LPDWORD lpBytesReturned, // 存放返回字节数的指针   

  9. LPOVERLAPPED lpOverlapped // 用于同步操作的Overlapped结构体指针   

  10. );  




DeviceIoControl 这个函数是重点中的重点,几乎所有应用层与内核层的通信与此函数有关,发送控制代码直接到指定的设备驱动程序,使相应的移动设备以执行相应的操作。

驱动程序可以通过CTL_CODE宏来组合定义一个控制码,并在IRP_MJ_DEVICE_CONTROL的实现中进行控制码的操作。在驱动层irpStack->Parameters.DeviceIoControl.IoControlCode表示了这个控制码。

说到这里我们不得不提的一个东西就是IRP:

IRP(IO请求包)用于win32和驱动程序通讯,NT内核有一个组件叫做IO管理器。IO管理器负责IRP的分发,驱动程序里创建好设备并且创建好符号链接后,Win32就可以加载驱动了。而要让一个驱动可以处理IRP,必需给驱动添加IRP处理例程。

添加的方法就是再DriverEntry里面对驱动对象DriverObject操作。该参数是一个指针,指向驱动对象,驱动对象内部有一个MajorFunction数组,该数组的类型是
NTSTATUS  (*PDRIVER_DISPATCH) (IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp) 。这是一个函数指针,指向每个IRP对于的处理例程。最后就是为所有需要处理的IRP实现对应的例程。

应用层通过DeviceIoControl 下发指令到内核层,内核层又通过对消息头的判断取出缓冲区的数据进行一系列的操作。

总得来说来说,有DeviceIoControl的存在,应用层向内核层传递消息就是一件轻松加愉快的事情,我简单的贴一点应用层和内核层的代码,大家可以做参考:

应用层:

[cpp] view plain copy

  1. BOOL bOK = ::DeviceIoControl(hAdapter, IOCTL_PTUSERIO_OPEN_ADAPTER,   

  2.                 pszAdapterName, nBufferLength, NULL, 0, &dwBytesReturn, NULL);  

  3. // 检查结果  

  4. if(!bOK)  

  5. {  

  6.     ::CloseHandle(hAdapter);  

  7.     return INVALID_HANDLE_VALUE;  

  8. }  

  9. return hAdapter;  


 


驱动层:

[cpp] view plain copy

  1. NTSTATUS DevIoControl(PDEVICE_OBJECT pDeviceObject, PIRP pIrp)  

  2. {  

  3.     // 假设失败  

  4.     NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;  

  5.   

  6.     // 取得此IRP(pIrp)的I/O堆栈指针  

  7.     PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp);  

  8.   

  9.     // 取得I/O控制代码  

  10.     ULONG uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;  

  11.     // 取得I/O缓冲区指针和它的长度  

  12.     PVOID pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;  

  13.     ULONG uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;  

  14.     ULONG uOutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;  

  15.       

  16.   

  17.     ULONG uTransLen = 0;  

  18.   

  19.     DBGPRINT((" DevIoControl... \n"));  

  20.   

  21.     switch(uIoControlCode)  

  22.     {  

  23.     case IOCTL_GET_SHARE_ADD:   


[cpp] view plain copy

  1. case IOCTL_PTUSERIO_OPEN_ADAPTER: // 根据控制码响应  

  2.         {  

然后驱动就可以从设定好的缓冲区里取数据进行操作。


第二步咱们来聊聊驱动层如何主动向应用层发消息:

很多情况下,我们需要驱动主动将消息传递给应用层,例如我们写一个网卡驱动,里面过滤传递的数据包,当数据包经过的时候,我们就需要驱动层将消息主动传递给应用层,这时候简单的DeviceIoControl 已经不能实现,那我们可以用共享事件加共享内存的方式来实现。

原理:通过Ring3创建事件,并将该事件传递给Ring0,同时Ring3创建监控线程,等待Ring0发起事件,此为应用层驱动层共享事件。同时Ring0在内核分配非分页内存,通过DeviceIoControl 传递给Ring3,此为应用层驱动层共享内存。因在DeviceIoControl 中传送Buffer,涉及到内核数据拷贝,大数据量下使用效率很低,故用共享内存的方法。

应用层代码:

[cpp] view plain copy

  1.    m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);     // 创建事件  

  2.   

  3. // 发事件给ring 0  

  4.   

  5. if (0 == DeviceIoControl(hFile, SET_EVENT, &m_hEvent, sizeof(HANDLE), NULL, 0, &uRetBytes,NULL))  

  6. {  

  7.   

  8.     CloseHandle(hFile);  

  9.     CloseHandle(m_hEvent);  

  10.     printf("SET_EVENT failed\n");  

  11.     return FALSE;  

  12. }  

  13.   

  14. // 获得ring 0 的共享内存  

  15. PVOID add = NULL;  

  16. if (0 == DeviceIoControl(hFile, IOCTL_GET_SHARE_ADD, NULL, 0, &add, sizeof (PVOID), &uRetBytes,NULL))  

  17. {  

  18.     CloseHandle(hFile);  

  19.     CloseHandle(m_hEvent);  

  20.     return FALSE;  

  21. }  

  22. m_pShareMem = (PVOID)add;  

  23. return TRUE;  


[cpp] view plain copy

  1.     while (1)  

  2.     {  

  3.         WaitForSingleObject(m_hEvent, INFINITE);  

  4.         {  

  5.   

  6. <span style="white-space:pre">            </span>//等待事件发生  

  7.         }  

  8.     }  


内核层:


[cpp] view plain copy

  1.     g_pSysAdd = ExAllocatePool(NonPagedPool, 2048*3);  

  2. g_pMdl = IoAllocateMdl(g_pSysAdd, 2048*3, FALSE, FALSE, NULL);  

  3. MmBuildMdlForNonPagedPool(g_pMdl);//建立共享内存  


[cpp] view plain copy

  1. UserAddr = MmMapLockedPagesSpecifyCache(g_pMdl, UserMode, NonPagedPool, NULL, FALSE, NormalPagePriority);  

[cpp] view plain copy

  1. *((PVOID*)pIoBuffer) = UserAddr;//将驱动建立的内存地址传递给应用  

[cpp] view plain copy

  1. memset(g_pSysAdd,0,2048*3);  

  2.    RtlCopyMemory(g_pSysAdd, test, (DataOffset - 54)*2);//将需要的数据写入内存  

  3.    KeSetEvent((PKEVENT)g_pEvent, 0,FALSE);//通知应用层可以读了  

接下来我们就可以在应用层读取里面的数据了,地址就是我们通过DeviceIocontrol得到的地址。


« 上一篇:wifi共享上网(至尊版wifi)
« 下一篇:drcom至尊版使用openwrt路由器拨号
在这里写下您精彩的评论
  • 微信

  • QQ

  • 支付宝

返回首页
返回首页 img
返回顶部~
返回顶部 img