首页
社区
课程
招聘
drv,*.vxd,*.sys,文件各自的用途和运用范围?
发表于: 2004-9-20 11:09 5646

drv,*.vxd,*.sys,文件各自的用途和运用范围?

2004-9-20 11:09
5646
drv,*.vxd,*.sys  这三种文件都是设备驱动文件。
开发工具一般是VC和DDK。CPU有4个ring级,内核运行在ring0,而普通程序运行在ring3,另外两个没有使用。.drv一般是打印机、串口等驱动,是运行在ring3级的。.vxd是Win9x/Windows 3.x使用的驱动程序,运行在ring0级。而.sys是Windows NT/2000的内核驱动,运行在ring0级。而Win98的WDM驱动也是.sys的。Win9x和WinNT的驱动通常不兼容。
  这写后缀名的变化,随之迩来的系统内核驱动模式的变化,以下是我的摘录:
设备驱动程序发展与简史最早的运行在Intel处理器芯片上的PC机,CPU提供给其640KB的所谓的“实”内存。这是因为内存是真正在那里以内存芯片的形式存在,并且可以依靠处理器的20位物理地址直接访问的。而处理器也只提供了一种模式,称之为实模式,其内部由处理器将两个16位的寄存器结合,组成20位的内存地址形式,给相应的涉及内存操作的指令使用。计算机体系结构高扩了扩充插槽的概念使得一些用户可以将买来的独立的板卡组装到机器上。这类板卡常常伴随有一些用于设置DIP开关的指令(随后,跳线出现),这些指令可以轻微地改变I/O配置。但你不得不了解你PC的所有I/O和中断分配来正确的做这件事情。MS-DOS基于CONFIG.SYS文件整合起了一种架构,使得操作系统能够装载原始设备和附加板卡的实模式设备驱动程序。所以,不可避免的就是这些驱动程序要使用汇编语言来编写并且或多或少地依赖于一定的BIOS中断指令和MS-DOS自身的系统服务。这样,最终用户不得不通过命令调用应用程序。而应用程序开发者也不得不为程序开发出视频显示,键盘,和鼠标程序,因为MS-DOS和系统BIOS都没有把其做的充分。
之后,IBM引入了基于80286处理器的AT系列的个人计算机。286处理器添加了保护模式作业,这样,这样程序可以访问的主内存和扩展内存达到16MB,其使用24位段地址(通过一个在16位段寄存器中的段选择器被间接指定)和一个16位的偏移地址。MS-DOS自己保持为一个实模式操作系统,所以当时许多软件厂商做过一些DOS的扩展产品来允许程序员将其实模式下的应用程序移到保护模式下并能访问到全部内存。自MS-DOS开始统治电脑以来,驱动程序技术基本上就没有前进。

改变PC机技术的分水岭在我看来,应当是――Intel发布其80386处理器芯片之日。386允许程序通过页表间接地访问访问高达4GB的虚拟内存地址,它还允许程序不费吹灰之力就能将32位数据用于算法和寻址上。那一阵子曾经让软件工具市场很狼狈,如编译器厂商和DOS扩展产品的公司…………设备驱动程序依旧是用汇编语言写的16位的实模式程序并且通过CONFIG.SYS安装,而终端用户依旧需要手动配置板卡

之后的处理器主要是在性能和速度上的提升,本质上并没有改变。如我写这章的时候,1GHz的CPU,50G的硬盘和512MB(乃至更高)的内存的计算机已经司空见惯了。

与之同时,操作系统平台上的反展也进行着。大多数人,甚至包括系统程序员,也更加喜欢图形交互式的计算机而不是字符式的了。实际上Microsoft在图形化操作系统上并没有抢先一步,Apple在其第一款Macintosh上倒是先发制人。但之后微软却用其Windows系列操作系统在这个领域上取得了霸主地位。一开始,Windows不过是一个实模式的MS-DOS下的一个图形模式的外壳,随着时间的过去,一批包括显示,键盘和鼠标的通用Windows驱动出现了。这些驱动是可执行文件,其扩展名为.DRV,主要使用汇编语言编写。

随着AT系列电脑的问世,Microsoft生产了一个有保护模式版本的Windows。但Microsoft还是在保护模式下使用实模式下的.DRV驱动。硬件除了标准的Windows设备(如显示,键盘和鼠标)外,继续沿用实模式下的MS-DOS驱动。

最终,在386处理器时代后不久,Micorsoft发布了Windows3.0,一个完全利用虚拟内存方式的使用“增强”模式作业的操作系统。虽然如此,但其新的硬件依旧需要实模式驱动。但现在已经有了大问题。若要多任务地执行MS-DOS程序(这是用户所需要的),Micorsoft不得不建立虚拟操作系统。每个MS-DOS应用程序在Windows的图形环境上运行在其自己的虚拟机上。但是所有这类的MS-DOS应用程序都可能会通过IN和OUT指令来直接访问硬件,读写设备存储器,处理来自硬件的中断。此外,两个或更多的应用程序共享处理器时间片将会导致硬件上的指令冲突。这类冲突当然也会在显示,键盘和鼠标上表现出来。
为了允许多种程序共享物理硬件,Microsoft引入了虚拟驱动设备的概念,其主要的目的是“虚拟”一个硬件设备。这样驱动被统一称之为VxD,因为其大部分的文件名适合VxD.386的特征。其中,x代表其管理的设备。利用这一概念,Windows 3.0创建了可以单独使用许多硬件设备的虚拟机外观。但由于设备自身的发展,大多数的时候它还得被实模式的MS-DOS驱动程序所驱动。VxD就扮演了 通过首先截取应用程序试图接触硬件的尝试,把应用程序的访问传递给了硬件,并暂时地将处理器切换到一种被称为虚拟8086的实模式下来运行MS-DOS驱动程序的这种操作。

这样其实也不是很好,使用模式切换来运行实模式的驱动……

Microsoft版本的OS/2最后成为了Windows NT,其第一次公开发行是在刚刚发布完Windows 3.1的上个世纪90年代。Microsoft构建Windows NT系统是想让其成为一种稳定和安全的平台。Windows NT的驱动使用一种崭新的内核模式技术。实际上其几乎没有参与当时流行的其它的两种驱动技术。Windows NT驱动程序完全使用了C语言编写,这样就可以在不改变任何代码的情况下在新的CPU架构上被重新编译。

另一件有关Windows 3.0 发展的事情,这已经成为我们今天的一个重要的分支。Windows 正式将软件世界分成了用户模式程序和内核模式程序。用户模式程序包括了用户买来使用的所有的应用程序和游戏。但其并不被信任能够健壮地(甚至是诚实地)来处理硬件或者其他程序。而内核模式程序则包括操作系统自身和大家写的全部的驱动程序。内核模式程序是被系统完全信赖的,并且可以接触到它们想要的系统的任何资源。虽然 Windows 3.0使用了其作业模式的隔离程序,但没有一个版本的Windows(甚至包括Windows Me)真正地引入内存保护使其成为一个安全的系统。安全是Windows NT和其后继者的主要职责,如禁止用户模式程序查看或者更改由内核管理的资源。

实际上PC机的平均计算能力直到最近才满足了Windows NT良好运行地需要。因此Microsoft不得不延长其它Windows的产品的使用周期。Windows 3.0成长为 3.1,3.11,和95。以Windows 95开始,如果你打算编写一个设备驱动,你就会去写一种被称为VxD的真正的32位保护模式的驱动程序。同样,从 Windows 95开始,终端用户可以丢掉他们的I/O图,因为操作系统新的即插即用的特点可以开始自动地识别并配置硬件了。但是作为一个硬件制造者,你还不得不为了那些不打算从Windows 3.X升级的用户编写一个实模式驱动。同时,Windows NT发展到了3.5,4.0。你可能需要第三方的驱动来支持这些系统,而且你需要在这些项目中使用你的编程知识。

之后,Micorsoft又开发了一个新的设备驱动技术――Windows 驱动模块(WDM),并且将其应用到了Windows 95后的Windows 98和Windows Me。它们也将此技术应用到了Windows NT后的Windows 2000和Windows XP。在Windows Me时,MS-DOS只是象征性的存在,并且不需要硬件制造者担心实模式的设备驱动。因为有WDM在,至少由原始的意图,实际同样在所有的平台上,写一个这样的驱动成为了可能。

总之,我们是站在最初的PC架构和早期的MS-DOS的基础上的。最终用户仍还会打开机箱,安装一些扩展的板卡,但和以前相比,现在我们可以使用一些不同的、更强有力的总线。即插即用和周边元件扩展接口(PCI)总线已让最终用户远离了I/O,内存和中断请求的使用。虽然现在还有BIOS,但其现在的主要作用只是引导系统并告诉操作系统(Windows XP或Windows Me)那些被发现的配置细节。WDM驱动程序的扩展名是.SYS,就和当初实模式下的驱动程序一样。

欢迎大家来阔充!
:p

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 1
支持
分享
最新回复 (3)
雪    币: 258
活跃值: (230)
能力值: ( LV12,RANK:770 )
在线值:
发帖
回帖
粉丝
2
开发WDM驱动程序来说,三个常用组合:
1. 直接使用Windows DDK
2. 使用DriverStudio
3. 使用Windriver
比较三种方式的优缺点。
第一种:,开发难度大一些,而且有很多烦琐的工作要作,大部分都是通用的基础性的工作。但如果选用这种方式的话你将对整个体系结构会有很好的理解和把握。
第二种:难度低一些,工具软件已经帮你作了很多基础性的工作。也封装了一些细节,你只要专心去作你需要的操作,但由于封装的问题,可能会带来一些bug。有可能导致项目的失败。
第三种:几乎没有难度(从开发驱动的角度)。很容易,但只能开发硬件相关的驱动,事实上你写的只是定制和调用它提供的通用驱动而已。效率上有问题。工作频率不是很高。但开发花费的时间很少。要实现的功能主要有以下几个:
初始化
创建和删除设备
I/O请求的超时处理
I/O请求的撤消
访问硬件资源
处理Windows的输入/输出请求
串行化对设备的访问
调用其它驱动程序
处理一个可热拔插的设备被加入或删除的情况
处理电源管理请求
使用Windows管理诊断功能
处理Windows的打开和关闭文件句柄的请求从实际工作情况来看,只有初始化模块是必不可少的。但是只有初始化模块的驱动程序什么工作也干不了,只能说它仅仅是一个概念意义上的驱动程序而已,好比失去感觉的植物人(躯体存在,但已经没有了意志)。通常情况下,一个完整的驱动程序至少要能响应用户态程序发出的I/O访问请求。大多数情况下驱动程序要访问它们所支持的硬件资源,并且要支持简单的电源管理功能和Windows管理诊断功能或能向系统日志写入信息。
WDM驱动程序通常由PnP管理器载入内存,然后调用它之中的AddDevice例程来创建设备。当然,在此时还要需要一个inf安装文件而来指明该驱动程序需要的一些参数。
系统内核通常通过向驱动程序发送IRP包来运行驱动程序中的实现代码。我们以Windows向设备发出的ReadFile调用为例:此时Windows向驱动程序发出一个“读”请求的IRP包,读取缓冲区的大小和位置作为IRP包中的参数指定(IRP实际上是一个数据结构,包含几个域)。如果你作过Windows的程序,特别是用 VC作过开发的话,你应该知道,windows用户态应用程序是消息驱动的,应用程序中的代码是通过消息机制的触发而获得运行的机会的,需要的参数是通过消息的域(wParam、lParam)传给应用程序。实际上驱动程序的动作还是可以看作是一种消息驱动方式,只不过内核态的“消息”已经不再称作消息,而是被称作I/O请求包(IRP)。
驱动程序通常使用DriverEntry作为入口点,与我们在Windows应用程序中定义的WinMain相似。通常情况下,它是驱动程序的默认入口点。
注: 标准Build脚本将驱动程序入口点定为DriverEntry,你最好遵守这个假设,否则必须修改Build脚本。
在这个入口函数中,我们必须作必要的初始化设置,并设置必要的回调函数。我们可以这样理解:我们用c++(特别是用VC++)时,我们在类的构造函数中要作必要的初始化操作,并要在类中作消息处理方法的映射,这样才能让需要的消息得到适当的处理。我们也要在DriverEntry例程中设置必要的IRP处理函数。
一般情况下,DriverEntry例程要设置以下几个IRP处理函数:
• DriverUnload 指向驱动程序的清除例程。I/O管理器会在卸载驱动程序前调用该例程。通常,WDM驱动程序的DriverEntry例程一般不分配任何资源,所以DriverUnload例程也没有什么清除工作要做。
• DriverExtension->AddDevice 指向驱动程序的AddDevice函数。PnP管理器将为每个硬件实例调用一次AddDevice例程。这样将创建一个该设备对象。
• DriverStartIo 如果驱动程序使用标准的IRP排队方式,应该设置该成员,使其指向驱动程序的StartIo例程。如果你不理解什么是“标准”排队方式,不要着急,能后的教程中你就会完全明白,许多驱动程序都使用这种方法。
• MajorFunction 是一个指针数组,I/O管理器把每个数组元素都初始化成指向一个空函数,这个空函数仅返回失败。驱动程序可能仅需要处理几种类型的IRP,所以至少应该设置与那几种IRP类型相对应的指针元素,使它们指向相应的派遣函数。
下面是一段DriverEntry例程的示例:
extern "C"
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
DriverObject->DriverUnload = DriverUnload; <--1
DriverObject->DriverExtension->AddDevice = AddDevice;
DriverObject->DriverStartIo = StartIo;
DriverObject->MajorFunction[IRP_MJ_PNP] = DispatchPnp; //设置各个IRP的处理函数 ,这三个IRP是每一个WDM驱动程序必须处理的。
DriverObject->MajorFunction[IRP_MJ_POWER] = DispatchPower;
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = DispatchWmi;
... <--3
servkey.Buffer = (PWSTR) ExAllocatePool(PagedPool, RegistryPath->Length + sizeof(WCHAR)); <--4
if (!servkey.Buffer)
return STATUS_INSUFFICIENT_RESOURCES;
servkey.MaximumLength = RegistryPath->Length + sizeof(WCHAR);
RtlCopyUnicodeString(&servkey, RegistryPath);
return STATUS_SUCCESS; <--5
}
1. 前三条语句为驱动程序的其它入口点设置了函数指针。在这里,我们用了能表达其功能的名字命名了这些函数:DriverUnload、AddDevice、StartIo。
2. 每个WDM驱动程序必须能处理PNP、POWER、SYSTEM_CONTROL这三种请求;应该在这里为这些请求指定派遣函数。在早期的Windows 2000 DDK中,IRP_MJ_SYSTEM_CONTROL曾被称作IRP_MJ_WMI,所以我把系统控制派遣函数命名为DispatchWmi。
3. 在省略号处,你可以插入设置其它MajorFunction指针的代码。
4. 如果驱动程序需要访问设备的服务键,可以在这里备份RegistryPath串。例如,如果驱动程序要作为WMI生产者,则需要备份这个串。这里我假设已经在某处声明了一个类型为UNICODE_STRING的全局变量servkey。
5. 返回STATUS_SUCCESS指出函数成功。如果函数失败,应该返回NTSTATUS.H中的一个错误代码,或者返回用户定义的错误代码。STATUS_SUCCESS的值为0。
关于DriverUnload例程的补充说明:
在WDM驱动程序中,DriverUnload例程的作用就是释放DriverEntry例程在全局初始化过程中申请的任何资源,但它几乎没什么可做。如果你在DriverEntry中备份了RegistryPath串,应该在这里释放备份所占用的内存:
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
RtlFreeUnicodeString(&servkey);//释放先前申请的资源
}
如果DriverEntry例程返回一个失败状态代码,系统将不再调用DriverUnload例程。所以,不能让DriverEntry例程出错后产生任何副作用,必须在它返回错误代码前消除副作用(释放掉申请的系统资源)。
一般情况下,一个驱动程序可以被多个设备利用。WDM驱动程序有一个特殊的AddDevice函数,PnP管理器为每个设备实例调用该函数。以下为该函数的原型定义:
NTSTATUS AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT pdo)
{
}
DriverObject参数指向一个驱动程序对象,就是你在DriverEntry入口例程中初始化的那个驱动程序对象。pdo参数指向设备堆栈底部的物理设备对象。
对于功能驱动程序,其AddDevice函数的基本职责是创建一个设备对象并把它连接到以pdo为底的设备堆栈中。相关步骤如下:
1. 调用IoCreateDevice创建设备对象,并建立一个私有的设备扩展对象。
2. 注册一个或多个设备接口,以便应用程序能够发现设备的存在。另外,还可以给出设备名并创建符号连接。
3. 初始化设备扩展和设备对象的Flag成员。
4. 调用IoAttachDeviceToDeviceStack函数把新设备对象放到堆栈上。
下面我将详细解释这些步骤。
创建设备对象
调用IoCreateDevice函数创建设备对象,例如:
PDEVICE_OBJECT fdo;
NTSTATUS status = IoCreateDevice(DriverObject,
sizeof(DEVICE_EXTENSION),
NULL,
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&fdo);
第一个参数(DriverObject) 就是AddDevice的第一个参数。该参数用于在驱动程序和新设备对象之间建立连接,这样I/O管理器就可以向设备发送指定的IRP。
第二个参数是设备扩展结构的大小。I/O管理器自动分配这个内存,并把设备对象中的DeviceExtension指针指向这块内存。
第三个参数在本例中为NULL。它可以是命名该设备对象的UNICODE_STRING串的地址。决定是否命名设备对象以及以什么名字命名还需要仔细考虑,我将在后面深入认真地讨论这个问题。
第四个参数(FILE_DEVICE_UNKNOWN) 是设备类型。这个值可以被设备硬件键(注册表中包含该硬件信息的键值)或类键(注册表中包含该类驱动信息的键值)中的可替换值(overriding values)所替代,如果这两个键都含有该参数的替换值,那么硬件键中的可替换值具有更高的优先权。对于属于某个已存在类的设备,必须在这些地方指定正确的值,因为驱动程序与外围系统的交互需要依靠这个值。另外,设备对象的默认安全设置也依靠这个设备类型值。
第五个参数(FILE_DEVICE_SECURE_OPEN) 为设备对象提供Characteristics标志。这些标志主要关系到块存储设备(如软盘、CDROM、Jaz等等)。未公开标志位FILE_AUTOGENERATED_DEVICE_NAME仅用于内部使用,并不是DDK文档忘记提到该标志。这个参数同样也能被硬件键或类键中的对应值替换,如果两个值都存在,那么硬件键中的可替换值具有更高的优先权。
第六个参数(FALSE) 指出设备是否是排斥的。通常,对于排斥设备,I/O管理器仅允许打开该设备的一个句柄。这个值同样也能被注册表中硬件键和类键中的值替换,如果两个可替换值都存在,硬件键中的可替换值具有更高的优先权。
注意
排斥属性仅关系到打开请求的目标是命名设备对象。如果你遵守Microsoft推荐的WDM驱动程序设计方针,没有为设备对象命名,那么打开请求将直接指向PDO(物理设备对象)。PDO通常不能被标记为排斥,因为总线驱动程序没有办法知道设备是否需要排斥特征。把PDO标为排斥的唯一的机会在注册表中,即设备硬件键或类键的Properties子键含有Exclusive可替换值。为了完全避免依赖排斥属性,你应该利用IRP_MJ_CREAT例程弹出任何有违规行为的打开请求。
第七个参数(&fdo) 是存放设备对象指针的地址,IoCreateDevice函数使用该变量保存刚创建的设备对象的地址。
如果IoCreateDevice由于某种原因失败,则它返回一个错误代码,不改变fdo中的值。如果IoCreateDevice函数返回成功代码,那么它同时也设置了fdo指针。然后我们进行到下一步,初始化设备扩展,做与创建新设备对象相关的其它工作,如果在这之后又发现了错误,那么在返回前应先释放刚创建的设备对象并返回状态码。见下面例子代码:
NTSTATUS status = IoCreateDevice(...);
if (!NT_SUCCESS(status))
return status;
...
if (<some other error discovered>)
{
IoDeleteDevice(fdo);
return status;
}
为设备命名
Windows 使用对象管理器集中管理系统中的大量的内部数据结构(每个对象在系统中都表现为一个数据结构),包括驱动程序对象和设备对象。为了便于区别,每个对象都有名称,对象管理器用一个层次化的命名空间来管理这些名称。图中是DevView(一个设备观察工具,在驱动开发网站<http://www.driverdevelop.com有下载>)显示的顶层对象名。此工具以文件夹形式显示的对象是目录对象,它可以包含子目录或常规对象,其它图标则代表正常对象。
通常设备对象都把自己的名字放到\Device目录中。在Windows 2000中,设备的名称有两个用途。第一个用途,通过命名后,其它内核模式部件可以通过调用IoGetDeviceObjectPointer函数找到该设备,找到设备对象后,就可以向该设备的驱动程序发送IRP(I/O请求包)。
另一个用途,允许用户态的应用程序打开命名设备的句柄,这样它们就可以向驱动程序发送IRP。应用程序可以使用标准的CreateFile API打开命名设备句柄,然后用ReadFile、WriteFile,和DeviceIoControl向驱动程序发出请求(关于这些API函数的详细说明和使用,我们将在后面的文章中详述)。应用程序打开设备句柄时使用\\.\路径前缀。在C/C++语言程序中使用时,需要转化为’\\\\.\\’,这是由语法规定的。在内部,I/O管理器在执行名称搜索前自动把\\.\转换成\??\。为了把\??目录中的名字与名字在其它目录(例如,在\Device目录)中的对象相连接,对象管理器实现了一种称为符号连接(symbolic link)的对象。
符号连接
符号连接有点象WIDOWS桌面上的快捷方式,符号连接在Windows NT/2K中的主要用途是把处于列表前面的DOS形式的名称连接到设备上。符号连接可以使对象管理器在分析一个名称时能跳到命名空间的某个地方。例如我们通常见到的C盘,其实它是就是一个设备(磁盘)的符号链接。如果你用过unix/linux操作系统的话,你会对符号链接有所理解,这里的符号链接相当于unix/linux系统中的软链接。

术语
类键
所有设备类的类键都出现在HKLM\System\CurrentControlSet\Control\Class键中。它们的键名是由Microsoft赋予的GUID值。
2004-9-20 11:21
0
雪    币: 212
活跃值: (70)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
顶 顶 顶!
2004-9-20 11:22
0
雪    币: 258
活跃值: (230)
能力值: ( LV12,RANK:770 )
在线值:
发帖
回帖
粉丝
4
windows用户态应用程序是消息驱动的,应用程序中的代码是通过消息机制的触发而获得运行的机会的,需要的参数是通过消息的域(wParam、lParam)传给应用程序。

IRP是一个数据结构,包含几个域。
实际上驱动程序的动作还是可以看作是一种消息驱动方式,只不过内核态的“消息”已经不再称作消息,而是被称作I/O请求包(IRP)。

大家可以通过这个工具 IRP trace 来监视底层的IRP消息包!
(其实在还有一个我忘记叫什么名字了在softice那个公司里有个监视vxd与WDM消息流,谁记的说一声吧!)
这个软件不是免费的,当可以用:http://www.skycn.com/soft/18054.html
:D :D :D
2004-9-20 11:41
0
游客
登录 | 注册 方可回帖
返回
//