虚拟设备驱动程序(VxD)设计中的两个关键问题

2023-12-09 02:09

本文主要是介绍虚拟设备驱动程序(VxD)设计中的两个关键问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

虚拟设备驱动程序(VxD)设计中的两个关键问题

  陈国友

  在虚拟设备驱动程序(VxD)的设计中,两个尤为关键,且又令人困扰的问题是VxD的虚拟化和VxD与应用程序间的通信机制。下面,对这两个问题作一详细的探讨。

  一、VxD的虚拟化

  由于Windows允许同时运行多个任务,所以出现多个进程试图同时访问同一物理设备的情况时,如果多个应用程序通过同一个DLL驱动程序(注意和虚拟设备驱动程序VxD的区别)访问设备,不需要对该设备虚拟化,驱动程序使之顺序访问;如果是多个Windows应用程序对相同设备同时访问,由于都运行于System VM(系统虚拟机),所以也不需要虚拟化,它们的访问将由一个驱动程序(Windows driver DLL)进行检测并使之串行化,而不是依靠VxD;如果多个VM试图访问同一设备,由于DOS应用程序能够直接操纵硬件,所以必须对该设备进行虚拟化,一个虚拟化设备的VxD负责可靠地检测多个VM试图访问同一设备的情况,并采取仲裁的策略来解决这种冲突。这里可能有以下几种解决方案:

  1、允许一个VM访问物理设备,同时忽略其它的VM。这是最简单的虚拟化形式。如VPD(Virtual Printer Device)。

  2、允许一个VM访问物理设备,同时为其它的VM虚拟化设备。如VKD(Virtual Keyboard Device)分配给一个VM,并使之获得物理键盘的访问权(包括键盘中断在内),对其它的VM而言,VKD只向它们提供一个空的键盘缓冲区。

  3、允许多个VM共享同一物理设备。尽管存在假象,但从VM的观点来看,这种方法与独享访问一样。如VDD(Virtual Display Device),使每一个Windows环境下的DOS VM认为是直接写入显存,其实只是被VDD映射到了一个窗口的缓冲区。

  4、VxD独立访问物理设备的同时,允许一个VM访问虚拟设备,这是最复杂的虚拟化形式。如VCD(Virtual Com Device),VCD缓冲区接收串行数据并通过映射中断透明地传给相应的一个VM,VM在中断处理过程中读取串口数据寄存器,这些数据的实质是VCD缓冲区已经接收的数据。

  与物理设备一样,硬件中断很多时候也必须虚拟化,这种情况更为复杂。虚拟化中断实质上就是将硬件产生的中断映射到需要它的每一个VM(不管该VM是否正在运行),替代VxD进行服务。在这里我们给出一个虚拟化中断的VxD实例的几个重要回调过程,并采用最简单的仲裁策略来解决访问冲突(见程序1)。

  typedef struct

  {

   IRQHANDLE IrqHandle;

   VMHANDLE VMOwner;

   Char DeviceName[8];

   BOOL bVMIsServicing;

  } DEVICE_CONTEXT;

  

  void _stdcall MaskChangHandler ( VMHANDLE hVM , IRQHANDLE hIRQ , BOOL bMasking )

   //当一个VM在中断控制器中屏蔽或打开中断hIRQ时,VPICD调用该过程

  {

   if ( !bMasking ) //若为打开中断

   {

   if ( !device.VMOwner )

   {

   device.VMOwner = hVM; //若无任何VM占有该中断,则将该中断的拥有权设为当前VM

   }

   else

   {

   if ( device.VMOwner != hVM )

   {

   device.VMOwner = SHELL_Resolve_Contention ( device.VMOwner , hVM , device.DeviceName );

   //若已有VM占有该中断,则用户可通过对话框在两者间作出选择

   }

   }

   VPICD_Physically_Unmask ( hIRQ ); //打开该物理中断

   }

   else

   {

   device.VMOwner = 0;

   VPICD_Physically_Mask ( hIRQ ); //屏蔽该物理中断

   }

  }

  

  BOOL _stdcall HwIntHandler ( VMHANDLE hVM , IRQHANDLE hIRQ )

   //当中断hIRQ发生,VPICD立即调用该过程

  {

   if ( device.VMOwner && !device.bVMIsServicing ) //若有VM占有该中断并且不在上一次的中断处理中

   {

   VPICD_Set_Int_Request ( device.VMOwner , hIRQ ); //请见本例程后的讨论

   }

   else

   {

   ......

   }

   return TRUE;

  }

  

  void _stdcall VirtIntHandler ( VMHANDLE hVM , IRQHANDLE hIRQ )

   //当VPICD每次向VM模拟中断时,调用该过程

  {

   device.bVMIsServicing = TRUE; //设置中断处理标志

  }

  

  void _stdcall IRETHandler ( VMHANDLE hVM , IRQHANDLE hIRQ )

   //当从VM的中断处理返回,执行该回调

  {

   device.bVMIsServicing = FALSE; //清除中断处理标志

   }

  (程序1)

  由于中断是异步产生的,所以当VxD调用VPICD(虚拟可编程中断控制器)服务VPICD_Set_Int_Request将该中断映射到VM时,该VM应处于执行状态。

  (1)在映射的第一步,VPICD通过调用VMM(虚拟机管理器)服务Call_Priority_VM_Event强制调度所希望的VM,使用最高的优先权(Time_Critical_Boost);

  (2)VPICD提供一个该服务的回调,所以当VM被调度运行时,VMM即可通知VPICD;

  (3)然后VPICD通过调用另一个VMM服务Simulate_Int来调整VM的运行环境。该服务将VM的CS、IP和标志寄存器压入VM的堆栈,从VM的中断向量表IVT取出新的CS、IP和标志寄存器,并且清除中断标志;

  (4)当VPICD从回调返回,并且VMM变回V86模式时,VM便立即执行已向VPICD注册的中断处理过程。

  编写虚拟化设备的VxD与编写非虚拟化设备的VxD有很大的不同,主要是它要用到一组完全不同的VMM和VxD服务。实际上,现在很多为新设备所编写的VxD根本就不再虚拟化,因为并没有DOS或Windows应用程序直接访问这些硬件。

  二、VxD与应用程序间的通信机制

  由于VxD并不仅仅处理硬件,所以在大多数情形下,VxD还向应用程序提供一个接口。通过该接口,应用程序就能够做与硬件有关的事情了。

  Windows 9x具有VxD与应用程序双向通信的机制。下面叙述的应用程序均指Win32应用程序。

  应用程序到VxD的通信机制是:VxD并不象Win16应用程序接口那样输出一个特殊的API过程(保护模式API过程或V86模式API过程)来支持应用程序,取而代之的是它的控制过程必须能够处理一个特殊的消息:W32_DEVICEIOCONTROL。VMM代替调用DeviceIoControl函数的应用程序向VxD发送此消息。消息参数可确定VxD消息响应函数、输入输出缓冲区指针及缓冲区大小,并绑定在DIOCPARAMETERS结构中。通过这一接口,不仅仅可以读写设备,而且还能在应用程序和VxD之间互传指针,从而达到特殊应用的目的。

  有时只需调用应用程序与VxD间的接口,便能及时获得所需信息和服务。但还有一些特殊情况,必须由VxD异步通知应用程序,这就需要用到VxD到应用程序的通信机制。

  VxD到应用程序的接口关系要比应用程序到VxD的接口关系复杂得多。其间有两种调用方法:一种是使用PostMessage函数。通过调用这一由外壳VxD(SHELL VxD)提供的新服务,便可通知应用程序;另一种是使用特殊的Win32技术。这种技术的独到之处在于Win32 API支持多线程。

  在Win32技术中,尽管采用的APC(Asynchronous Procedure Calls)异步过程调用机制和Win32事件机制都依赖于唤醒一个Win32应用程序线程,但仍略有不同。VxD到应用程序最简单的通信机制就是通过APC,这种方法对应用程序和VxD相对要简单一些。应用程序首先动态加载VxD(CreateFile),并用DeviceIoControl将回调函数的地址传给VxD,然后应用程序执行Win32调用SleepEx将其自身置为“挂起”(asleep yet alertable)状态时。当应用程序处于“挂起”状态,VxD能够通过VWIN32 VxD提供的QueueUserApc服务调用应用程序的回调函数。另一种更有效的方法是使用Win32事件机制。如果应用程序运行多个线程,当子线程等待着VxD来唤醒它的同时,主线程能够继续做自己的工作。例如,当一个子线程在等待VxD缓存接收的数据时,主线程可同时监控用户的输入。一旦缓冲区达到门限,VxD将唤醒等待的子线程。对于线程间的通知,VxD使用线程事件,就象应用程序的多线程机制所做的那样。在Windows 95下,VxD可访问与多线程应用程序非常相同的一些Win32事件API(由VWIN32 VxD提供)。这些VWIN32事件服务包括:_VWIN32_ResetWin32Event、_VWIN32_SetWin32Event、_VWIN32_PulseWin32Event、_VWIN32_WaitSingleObject、_VWIN32_WaitMultipleObjects。利用这些服务,VxD可唤醒一个等待的Win32应用程序线程,或是等待被一个Win32应用程序线程唤醒。不幸的是VxD不只是通过简单调用相应的事件服务,就能够获得Win32事件的句柄。因此,为获得Win32事件的句柄要涉及到一个复杂的过程和一个未公布的系统调用。事件通常是由应用程序产生(Win32 API CreateEvent),然后使用未公布的Win32 API函数OpenVxDHandle将获得的事件句柄转换为VxD事件句柄,再通过DeviceIoControl将这一ring 0级事件句柄传给VxD,于是VxD便可将其作为VWIN32事件函数的参数来使用。

  因为Windows采用基于消息的事件驱动机制,而VxD并不提供直接发往应用程序线程的消息,所以PostMessage所发消息与其它众多的消息都在主线程的消息循环中处理。这样,当执行一些界面操作时,大量的消息占据了消息队列,VxD所发送的消息就有可能得不到相应的处理。为解决这一问题,在实际设计中可采用的方法有两种:第一种是仍采用PostMessage,但在应用程序和VxD中需设置标志位,判断消息是否被处理并作了相应的工作(如重传数据);第二种是使用Win32事件机制,将一个线程专用于等待响应Win32事件,而另一些线程用于其它处理。下面给出VxD利用Win32事件机制激活Win32应用程序线程的部分例程(见程序2)。当生成或消除(destroy)一个VM,VxD便通知注册的应用程序,并显示出相应的信息。

  在VxD中,

  DWORD OnW32Deviceiocontrol ( PDIOCPARAMETERS p ) //VxD与Win32应用程序的接口函数

  {

   DWORD rc;

   swirch ( p->dwIoControlCode )

   {

   case DIOC_OPEN: //系统定义功能号:设备打开

   rc = 0;

   break;

   case DIOC_CLOSEHANDLE: //设备关闭

   bClientRegistered = FALSE;

   rc = 0;

   break;

   case EVENTVXD_REGISTER: //自定义功能号

   hWin32Event = p->lpvInBuffer;

   *( (DWORD *) (p->lpvOutBuffer) ) = (DWORD) & GlobalVMInfo;

   *( (DWORD *) (p->lpcbBytesReturned) ) = sizeof (DWORD);

   bClientRegistered = TRUE;

   rc = 0;

   break;

   default:

   rc = 0xffffffff;

   }

   return rc; //若返回0表示成功

  }

  

  BOOL OnVmInit ( VMHANDLE hVM ) //一旦有VM被初始化便执行

  {

   if ( bClientRegistered )

   {

   GlobalVMInfo.hVM = hVM;

   GlobalVMInfo.bVmCreated = TRUE;

   Call_Priority_VM_Event (LOW_PRI_DEVICE_BOOST , Get_Sys_VM_Handle() ,

   PEF_WAIT_FOR_STI+PEF_WAIT_NOT_CRIT ,

   hWin32Event , PriorityEventThunk , 0 );

  //使System VM为当前运行状态,将Ring0级事件句柄作为回调过程的参数

   }

   return TRUE;

  }

  

  VOID OnVmTerminate ( VMHANDLE hVM ) //一旦有VM被终结便执行

  {

   if ( bClientRegistered )

   {

   GlobalVMInfo.hVM = hVM;

   GlobalVMInfo.bVmCreated = FALSE;

   Call_Priority_VM_Event (LOW_PRI_DEVICE_BOOST , Get_Sys_VM_Handle() ,

   PEF_WAIT_FOR_STI+PEF_WAIT_NOT_CRIT ,

   hWin32Event , PriorityEventThunk , 0 );

   }

  }

  

  VOID _stdcall PriorityEventHandler ( VMHANDLE hVM , PVOID Refdata , CRS * pRegs )

  {

   HANDLE hWin32Event = Refdata;

   _VWIN32_SetWin32Event ( hWin32Event ); //激活事件对象

  }

  

  在Win32应用程序中;

  VOID main ( int ac , char *av[ ] )

  {

   hEventRing3 = CreateEvent ( 0 , FALSE , FALSE , NULL ); //生成Ring3级事件句柄

   if ( !hEventRing3 )

   {

   printf ( "Cannot create Ring3 event/n" );

   exit ( 1 );

   }

  

   hKernel32Dll = LoadLibrary ( "kernel32.dll" );

   if ( !hKernel32Dll )

   {

   printf ( "Cannot load KERNEL32.DLL/n" );

   exit ( 1 );

   }

  

   pfOpenVxDHandle = ( HANDLE ( WINAPI * ) ( HANDLE ) )

  GetProcAddress ( Kernel32Dll , "OpenVxDHandle" );

  If ( !pfOpenVxDHandle )

  {

   printf ( "Cannot get addr of OpenVxDHandle/n" );

   exit ( 1 );

  }

  

   hEventRing0 = (*pfOpenVxDHandle ) ( hEventRing3 ); //将Ring3级事件句柄转换为Ring0级事件句柄

   if ( !hEventRing0 )

   {

   printf ( "Cannot create Ring0 event/n" );

   exit ( 1 );

   }

  

   hDevice = CreateFile ( VxDName , 0 , 0 , 0 , CREATE_NEW , FILE_FLAG_DELETE_ON_CLOSE , 0 );

  //动态加载VxD

   if ( !hDevice )

   {

   printf ( "Cannot load VxD error = %x/n" , GetLastError ( ) );

   exit ( 1 );

   }

  

   if ( !DeviceIoControl ( hDevice , EVENTVXD_REGISTER , hEventRing0 , sizeof ( hEventRing0 ) , &pVMInfo , sizeof ( pVMInfo ) , &cbBytestReturned , 0 ) )

   //Win32程序与VxD的接口函数,将Ring0级事件句柄传入VxD,从VxD传出GlobalVMInfo结构的指针

   {

   printf ( "DeviceIoControl REGISTER failed/n" );

   exit ( 1 );

   }

  

   CreateThread ( 0 , 0x1000 , SecondThread , hEventRing3 , 0 , &tid ); //创建线程

   printf ( "Press any key to exit..." );

   getch ( );

   CloseHandle ( hDevice );

  }

  

  DWORD WINAPI SecondThread ( PVOID hEventRing3 )

  {

   while ( TRUE )

   {

   WaitForSingleObject ( ( HANDLE ) hEventRing3 , INFINITE ); //等待相应事件

   printf ( "VM %081x was %x" , pVMInfo->hVM , pVMInfo->bCreated ? "created": "destroyed" );

   //显示被created或destroyed的VM

   }

   return 0;

  }

  (程序2)

  三、结束语

  虽然VxD的设计还涉及到其它许多方面,但掌握解决以上关键问题的细节将对VxD的编程起到十分重要的作用。希望本文能给大家带来一些帮助。

  (作者地址:南京通信工程学院研究生二队 210016 收稿日期:1999.2.2)

这篇关于虚拟设备驱动程序(VxD)设计中的两个关键问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/472111

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

怎么让1台电脑共享给7人同时流畅设计

在当今的创意设计与数字内容生产领域,图形工作站以其强大的计算能力、专业的图形处理能力和稳定的系统性能,成为了众多设计师、动画师、视频编辑师等创意工作者的必备工具。 设计团队面临资源有限,比如只有一台高性能电脑时,如何高效地让七人同时流畅地进行设计工作,便成为了一个亟待解决的问题。 一、硬件升级与配置 1.高性能处理器(CPU):选择多核、高线程的处理器,例如Intel的至强系列或AMD的Ry

基于51单片机的自动转向修复系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订阅👇🏻 单片机

如何编写Linux PCIe设备驱动器 之二

如何编写Linux PCIe设备驱动器 之二 功能(capability)集功能(capability)APIs通过pci_bus_read_config完成功能存取功能APIs参数pos常量值PCI功能结构 PCI功能IDMSI功能电源功率管理功能 功能(capability)集 功能(capability)APIs int pcie_capability_read_wo

两个月冲刺软考——访问位与修改位的题型(淘汰哪一页);内聚的类型;关于码制的知识点;地址映射的相关内容

1.访问位与修改位的题型(淘汰哪一页) 访问位:为1时表示在内存期间被访问过,为0时表示未被访问;修改位:为1时表示该页面自从被装入内存后被修改过,为0时表示未修改过。 置换页面时,最先置换访问位和修改位为00的,其次是01(没被访问但被修改过)的,之后是10(被访问了但没被修改过),最后是11。 2.内聚的类型 功能内聚:完成一个单一功能,各个部分协同工作,缺一不可。 顺序内聚:

SprinBoot+Vue网络商城海鲜市场的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍:CSDN认证博客专家,CSDN平台Java领域优质创作者,全网30w+

单片机毕业设计基于单片机的智能门禁系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍程序代码部分参考 设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订

Spring的设计⽬标——《Spring技术内幕》

读《Spring技术内幕》第二版,计文柯著。 如果我们要简要地描述Spring的设计⽬标,可以这么说,Spring为开发者提供的是⼀个⼀站式的轻量级应⽤开发框架(平台)。 作为平台,Spring抽象了我们在 许多应⽤开发中遇到的共性问题;同时,作为⼀个轻量级的应⽤开发框架,Spring和传统的J2EE开发相⽐,有其⾃⾝的特点。 通过这些⾃⾝的特点,Spring充分体现了它的设计理念:在

开题报告中的研究方法设计:AI能帮你做什么?

AIPaperGPT,论文写作神器~ https://www.aipapergpt.com/ 大家都准备开题报告了吗?研究方法部分是不是已经让你头疼到抓狂? 别急,这可是大多数人都会遇到的难题!尤其是研究方法设计这一块,选定性还是定量,怎么搞才能符合老师的要求? 每次到这儿,头脑一片空白。 好消息是,现在AI工具火得一塌糊涂,比如ChatGPT,居然能帮你在研究方法这块儿上出点主意。是不

创业者该如何设计公司的股权架构

本文来自七八点联合IT橘子和车库咖啡的一系列关于设计公司股权结构的讲座。 主讲人何德文: 在公司发展的不同阶段,创业者都会面临公司股权架构设计问题: 1.合伙人合伙创业第一天,就会面临股权架构设计问题(合伙人股权设计); 2.公司早期要引入天使资金,会面临股权架构设计问题(天使融资); 3.公司有三五十号人,要激励中层管理与重要技术人员和公司长期走下去,会面临股权架构设计问题(员工股权激