驱动程序创建设备对象一共有三种读写方式,分别是缓冲区读写、直接方式读写和其他方式读写。这三种方式对应的设备对象的Flags子域分别是DO_BUFFERED_IO、DO_DIRECT_IO和0。
为什么要设置缓冲区读写方式?读写操作一般由WriteFile或ReadFile函数引起,以WriteFile为例,该函数要求用户提供一段缓冲区,并且说明缓冲区大小,然后WriteFile将这段内存的数据传入到驱动程序中。由于这段缓冲区内存是用户模式的内存地址,因此如果驱动程序直接引用该段内存是十分危险的。因此,就产生了使用缓冲区读写方式。具体是操作系统将应用程序提供的缓冲区的数据复制到内核模式下的地址中。这种方法的有点是,比较简单地解决了将用户地址传入驱动的问题,缺点是需要在用户模式和内核模式之间复制数据,影响了运行效率,因此,在少量 的内存操作时,可以考虑采取这个方法。
以缓冲区方式写设备时,操作系统将WriteFile提供的用户模式的缓冲区复制到内核模式地址下。这个地址由WriteFile创建的IRP的AssociatedIrp.SystemBuffer子域记录。
以“缓冲区”方式读设备时,操作系统会分配一段内核模式下的内存。这段内存大小等于ReadFile或者WriteFile指定的字节数,并且ReadFile或者WriteFile创建的IRP的AssociatedIrp.SystemBuffer子域会记录这段内存地址。当IRP请求结束时,这段内存地址会被复制到ReadFile提供的缓冲区中。在派遣函数中,可以通过IO_STACK_LOCATION中的Parameters.Read.Length子域知道ReadFile请求多少字节。通过IO_STACK_LOCATION中的Parameters.Write.Length子域知道WriteFile请求了多少字节。
然后,WriteFile和ReadFile指定对设备操作多少字节,并不意味着操作了这么多的字节。在派遣函数中,应设置IRP的子域IoStatus.Information。这个子域记录设备实际操作了多少字节。