开发驱动程序的过程[2]

 

编码技术与习惯 

  编写一个信任的内核模式部分和编写应用程序是不同的。这个部分提供一些编码技术与习惯,使编写代码更加容易一些。 

一般性的建议 

首先,在编写驱动程序的时候应当遵循一些一般性的指导方针: 

1.     1.  尽量避免使用汇编语言。因为它使代码难于阅读,没有移植性,维护困难。HAL宏提供一个安全的机制去访问I/O设备寄存器。因此,在驱动程序中级少使用汇编语言。 

2. 对于特定平台的代码,提供一个单独的模块,至少要用#ifdef/#endif语句将它们括起来。 

3.     3.  不要使用标准的C运行时库连接驱动程序。它除了浪费存储器空间之外,一些库的例程的状态或者上下文环境不是线程安全或者驱动程序安全的方式。 

4.     4.  这一条可能不适合编写设备驱动程序。天天使用运行库环境的C语言程序员,常常不清楚C程序和C运行库的差别。C运行库需要初始化,它尝试初始化一个堆区域和调用全局对象的构造器(使用C++时)。所有的这些任务防碍驱动程序的操作。 

5.     5.  Windows 2000提供它自己的环境支持内核模式代码。包括RtlXxx函数(运行库)等多个公用的C语言运行库服务支持。 

6.     6.  用某种源代码控制管理驱动程序工程。微软的Visual Source Safe是一个好的选择。对于大的跨平台的工程Rational公司的ClearCase也值得考虑。 

命名规范 

  所有的大的软件工程应该为例程和变量定义制定一些标准的命名规范,设备驱动程序也不例外。好的命名规范提高开发,调试,除错和维护的效率。 

  微软为DDK提供一个命名规范,NTDDK.H定义所有的数据类型,结构,常数,宏。按照DDK规范,所有这些类型的名字都是大写的。甚至是C语言数据类型也提供一个相应的DDK名字。例如,C语言的数据类型void*在NTDDK.H中是PVOID。这些定义可以很容易的扩展到未来的64Bits的平台。 

微软推荐将每个驱动程序例程的名字加上一个特殊的前缀。例如,编写一个鼠标类驱动程序,Start I/O例程的名字可能是 MouseClassStartIo。同样的缩写成为两个或者三个字符通常用在内部数据的命名。这样一个例程的名字可能是 MouConfiguration。 

头文件 

  除了包含NTDDK.h或者WDM.h,一个驱动程序应当使用私有的头文件去隐藏硬件和平台依赖的变量。例如,寄存器访问宏应该存储在一个私有的头文件,这些宏应该被#ifdef包括。这个技术解决在不同的I/O空间和存储器空间访问寄存器的问题。 

甚至可解决移植性,寄存器访问宏是驱动程序容易阅读和维护。下列代码片段是一个并行端口设备访问的宏,这个实例假定驱动程序的一些初始化代码已经把第一个设备寄存器的地址放到设备Extension的 PortBase中。 

// 定义设备计存器为偏移地址 

#define PAR_DATA      0       

#define PAR_STATUS    1       

#define PAR_CONTROL   2 

//为寄存器定义访问宏,每个宏使用指向设备Extension的指针作为参数 

#deinfe ParWriteData( pDevExt, bData ) \ 

(WRITE_PORT_UCHAR( pDevExt->PortBase + PAR_DATA, bData ) ) 

#define ParReadStatus( pDevExt )    \ 

(READ_PORT_UCHAR( pDevExt->>PortBase + PAR_STATUS )) 

#define ParWriteControl( pDevExt, bData )  \ 

(WRITE_PORT_UCHAR( pDevExt->PortBase + PAR_CONTROL, bData ) ) 

状态返回值 

  WIN2000的内核模式部分使用32Bits的状态值去描述一个特殊操作的结果。这些代码的数据类型是NTSTATUS,在三种情况下使用这个状态代码。 

  1. 使用任何WIN2000内部函数的时候,NTSTATUS值汇报调用成功或者失败。 

  2. I/O管理器调用一个驱动程序支持的回调例程,例程通常返回一个NTSTATUS值给系统。 

  3. 在完成I/O请求的处理之后,驱动程序必须用一个NTSTATUS值标记IRP,这个值最终被映 射到一个Win32 ERROR_XXX代码。NTSTATUS代码于Win32 error代码不是同一回事。I/O管理器提供它两个之间的映 射。DDK的帮助描述它们的映 射关系,它值得一读。 

NTSTATUS.h中描述大量的NTSTATUS值的符号名。所有的这些名字是STATUS_XXX的形式,XXX描述真正的状态信息。STATUS_SUCCESS,STATUS_NAME_EXISTS都是这些名字的实例。 

当一个系统例程返回一个NTSTATUS值,DDK头文件提供一个方便的宏去测试调用的成功于失败信息。下面的代码片段解释了这个技术: 

    NTSTATUS status; 

status = IoCreateDevice ( ... ); 

if ( !NT_SUCCESS( status )) {      // 产生错误,清除和退出 

                                       : } 

    一定要检查系统例程调用的返回值,否则错误将传送给驱动程序代码的其它部分,也可能是系统代码。尽早的捕捉到错误是软件工程师的主要的规则。 

投 票

觉得本文不错,投一票   

评 论


验证码: 看不清?换一张