驱动层和应用层进行通信
2015-10-09 13:01
211 查看
驱动层和应用层通信,主要是靠DeviceIoControl函数,下面是该函数的原型:
BOOL DeviceIoControl (
HANDLE hDevice, // 设备句柄
DWORD dwIoControlCode, // IOCTL请求操作代码
LPVOID lpInBuffer, // 输入缓冲区地址
DWORD nInBufferSize, // 输入缓冲区大小
LPVOID lpOutBuffer, // 输出缓冲区地址
DWORD nOutBufferSize, // 输出缓冲区大小
LPDWORD lpBytesReturned, // 存放返回字节数的指针
LPOVERLAPPED lpOverlapped // 用于同步操作的Overlapped结构体
);
hDevice是通过CreateFile函数来获取的。
dwIoControlCode
要进行操作的控制码。
驱动程序可以通过CTL_CODE宏来组合定义一个控制码,
并在IRP_MJ_DEVICE_CONTROL的实现中进行控制码的操作。在驱动层,irpStack->Parameters.DeviceIoControl.IoControlCode表示了这个控制码。
IOCTL请求有四种缓冲策略,下面一一介绍。
1、 输入输出缓冲I/O(METHOD_BUFFERED)
2、 直接输入缓冲输出I/O(METHOD_IN_DIRECT)
3、 缓冲输入直接输出I/O(METHOD_OUT_DIRECT)
4、 上面三种方法都不是(METHOD_NEITHER)
“缓冲”方法(METHOD_BUFFERED)
备注:
“输入”表示数据从应用层到驱动层
“输出”表示数据从驱动程序到应用程序
对于读取请求,I/O 管理器分配一个与用户模式的缓冲区大小相同的系统缓冲区。IRP 中的 SystemBuffer 字段包含系统地址。UserBuffer 字段包含初始的用户缓冲区地址。当完成请求时,I/O 管理器将驱动程序已经提供的数据从系统缓冲区复制到用户缓冲区。对于写入请求,会分配一个系统缓冲区并将 SystemBuffer 设置为地址。用户缓冲区的内容会被复制到系统缓冲区,但是不设置 UserBuffer。对于 IOCTL 请求,会分配一个容量大小足以包含输入缓冲区或输出缓冲区的系统缓冲区,并将 SystemBuffer 设置为分配的缓冲区地址。输入缓冲区中的数据复制到系统缓冲区。UserBuffer 字段设置为用户模式输出缓冲区地址。内核模式驱动程序应当只使用系统缓冲区,且不应使用 UserBuffer 中存储的地址。
对于 IOCTL,驱动程序应当从系统缓冲区获取输入并将输出写入到系统缓冲区。当完成请求时,I/O 系统将输出数据从系统缓冲区复制到用户缓冲区。
“直接”方法(METHOD_IN/OUT_DIRECT)
对于读取和写入请求,用户模式缓冲区会被锁定,并且会创建一个内存描述符列表 (MDL)。
MDL 地址会存储在 IRP 的 MdlAddress 字段中。SystemBuffer 和 UserBuffer 均没有任何含义。但是,驱动程序不应当更改这些字段的值。
对于 IOCTL 请求,如果在 METHOD_IN_DIRECT 和 METHOD_OUT_DIRECT 中同时有一个输出缓冲区,则分配一个系统缓冲区(SystemBuffer 又有了地址)并将输入数据复制到其中。如果有一个输出缓冲区,且它被锁定,则会创建 MDL 并设置 MdlAddress。UserBuffer 字段没有任何含义。
“两者都不”方法(METHOD_NEITHER)
对于读取和写入请求,UserBuffer 字段被设置为指向初始的用户缓冲区。不执行任何其他操作。SystemAddress 和 MdlAddress 没有任何含义。对于 IOCTL 请求,I/O 管理器将 UserBuffer 设置为初始的用户输出缓冲区,而且,它将当前 I/O 栈位置的 Parameters.DeviceIoControl.Type3InputBuffer 设置为用户输入缓冲区。利用该 I/O 方法,由驱动程序来确定如何处理缓冲区:分配系统缓冲区或创建 MDL。
通常,驱动程序在访问用户数据时不应当将 UserBuffer 字段用作地址,即使当用户缓冲区被锁定时也是如此。这是由于在调用驱动程序时,在系统中可能看不到调用用户的地址空间。(对于该规则的一个例外是,在最高层驱动程序将 IRP 向下传递到较低层的驱动程序之前,它可能需要使用 UserBuffer 来复制数据。)如果使用”直接”或”两者都不”方法,在创建 MDL 之后,驱动程序可以使用 MmGetSystemAddressForMdl 函数来获取有效的系统地址以访问用户缓冲区。
在驱动层,依传输类型的不同,输入缓冲区的位置亦不同,见下表。
传输类型 位置
METHOD_IN_DIRECT irp->AssociatedIrp.SystemBuffer
METHOD_OUT_DIRECT irp->AssociatedIrp.SystemBuffer
METHOD_BUFFERED irp->AssociatedIrp.SystemBuffer
METHOD_NEITHER irpStack->Parameters.DeviceIoControl.Type3InputBuffer
在驱动层,依传输类型的不同,输出缓冲区的位置亦不同,见下表。
传输类型 位置
METHOD_IN_DIRECT irp->MdlAddress
METHOD_OUT_DIRECT irp->MdlAddress
METHOD_BUFFERED irp->AssociatedIrp.SystemBuffer
METHOD_NEITHER irp->UserBuffer
//获取irp堆栈
pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
//获取用户缓冲区
pUserBuffer = pIrp->AssociatedIrp.SystemBuffer;
//获取控制码
ulIoCtrlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
//输入缓冲区大小
ulInputSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
//输出缓冲区大小
ulOutputSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
思考:
一般都采用缓冲区模式,那么什么时候采用直接模式进行读写吗?
BOOL DeviceIoControl (
HANDLE hDevice, // 设备句柄
DWORD dwIoControlCode, // IOCTL请求操作代码
LPVOID lpInBuffer, // 输入缓冲区地址
DWORD nInBufferSize, // 输入缓冲区大小
LPVOID lpOutBuffer, // 输出缓冲区地址
DWORD nOutBufferSize, // 输出缓冲区大小
LPDWORD lpBytesReturned, // 存放返回字节数的指针
LPOVERLAPPED lpOverlapped // 用于同步操作的Overlapped结构体
);
hDevice是通过CreateFile函数来获取的。
dwIoControlCode
要进行操作的控制码。
驱动程序可以通过CTL_CODE宏来组合定义一个控制码,
define CTRLCODE_BASE 0x8000
define MYCTRL_CODE(i) \
CTL_CODE(FILE_DEVICE_UNKNOWN,CTRLCODE_BASE + i, METHOD_BUFFERED, FILE_ANY_ACCESS)并在IRP_MJ_DEVICE_CONTROL的实现中进行控制码的操作。在驱动层,irpStack->Parameters.DeviceIoControl.IoControlCode表示了这个控制码。
IOCTL请求有四种缓冲策略,下面一一介绍。
1、 输入输出缓冲I/O(METHOD_BUFFERED)
2、 直接输入缓冲输出I/O(METHOD_IN_DIRECT)
3、 缓冲输入直接输出I/O(METHOD_OUT_DIRECT)
4、 上面三种方法都不是(METHOD_NEITHER)
“缓冲”方法(METHOD_BUFFERED)
备注:
“输入”表示数据从应用层到驱动层
“输出”表示数据从驱动程序到应用程序
对于读取请求,I/O 管理器分配一个与用户模式的缓冲区大小相同的系统缓冲区。IRP 中的 SystemBuffer 字段包含系统地址。UserBuffer 字段包含初始的用户缓冲区地址。当完成请求时,I/O 管理器将驱动程序已经提供的数据从系统缓冲区复制到用户缓冲区。对于写入请求,会分配一个系统缓冲区并将 SystemBuffer 设置为地址。用户缓冲区的内容会被复制到系统缓冲区,但是不设置 UserBuffer。对于 IOCTL 请求,会分配一个容量大小足以包含输入缓冲区或输出缓冲区的系统缓冲区,并将 SystemBuffer 设置为分配的缓冲区地址。输入缓冲区中的数据复制到系统缓冲区。UserBuffer 字段设置为用户模式输出缓冲区地址。内核模式驱动程序应当只使用系统缓冲区,且不应使用 UserBuffer 中存储的地址。
对于 IOCTL,驱动程序应当从系统缓冲区获取输入并将输出写入到系统缓冲区。当完成请求时,I/O 系统将输出数据从系统缓冲区复制到用户缓冲区。
“直接”方法(METHOD_IN/OUT_DIRECT)
对于读取和写入请求,用户模式缓冲区会被锁定,并且会创建一个内存描述符列表 (MDL)。
MDL 地址会存储在 IRP 的 MdlAddress 字段中。SystemBuffer 和 UserBuffer 均没有任何含义。但是,驱动程序不应当更改这些字段的值。
对于 IOCTL 请求,如果在 METHOD_IN_DIRECT 和 METHOD_OUT_DIRECT 中同时有一个输出缓冲区,则分配一个系统缓冲区(SystemBuffer 又有了地址)并将输入数据复制到其中。如果有一个输出缓冲区,且它被锁定,则会创建 MDL 并设置 MdlAddress。UserBuffer 字段没有任何含义。
“两者都不”方法(METHOD_NEITHER)
对于读取和写入请求,UserBuffer 字段被设置为指向初始的用户缓冲区。不执行任何其他操作。SystemAddress 和 MdlAddress 没有任何含义。对于 IOCTL 请求,I/O 管理器将 UserBuffer 设置为初始的用户输出缓冲区,而且,它将当前 I/O 栈位置的 Parameters.DeviceIoControl.Type3InputBuffer 设置为用户输入缓冲区。利用该 I/O 方法,由驱动程序来确定如何处理缓冲区:分配系统缓冲区或创建 MDL。
通常,驱动程序在访问用户数据时不应当将 UserBuffer 字段用作地址,即使当用户缓冲区被锁定时也是如此。这是由于在调用驱动程序时,在系统中可能看不到调用用户的地址空间。(对于该规则的一个例外是,在最高层驱动程序将 IRP 向下传递到较低层的驱动程序之前,它可能需要使用 UserBuffer 来复制数据。)如果使用”直接”或”两者都不”方法,在创建 MDL 之后,驱动程序可以使用 MmGetSystemAddressForMdl 函数来获取有效的系统地址以访问用户缓冲区。
在驱动层,依传输类型的不同,输入缓冲区的位置亦不同,见下表。
传输类型 位置
METHOD_IN_DIRECT irp->AssociatedIrp.SystemBuffer
METHOD_OUT_DIRECT irp->AssociatedIrp.SystemBuffer
METHOD_BUFFERED irp->AssociatedIrp.SystemBuffer
METHOD_NEITHER irpStack->Parameters.DeviceIoControl.Type3InputBuffer
在驱动层,依传输类型的不同,输出缓冲区的位置亦不同,见下表。
传输类型 位置
METHOD_IN_DIRECT irp->MdlAddress
METHOD_OUT_DIRECT irp->MdlAddress
METHOD_BUFFERED irp->AssociatedIrp.SystemBuffer
METHOD_NEITHER irp->UserBuffer
//获取irp堆栈
pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
//获取用户缓冲区
pUserBuffer = pIrp->AssociatedIrp.SystemBuffer;
//获取控制码
ulIoCtrlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
//输入缓冲区大小
ulInputSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
//输出缓冲区大小
ulOutputSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
思考:
一般都采用缓冲区模式,那么什么时候采用直接模式进行读写吗?
相关文章推荐
- linux 修改密码
- iOS--字符串拼接
- DirectDraw创建Windows窗口
- yum
- uitabBar上面的各定义选中时的图片颜色。不要渲染
- Linux下安装maven
- Android典型界面设计(8) ——ViewPager+PagerSlidingTabStrip实现双导航
- 使用C语言描述静态链表和动态链表
- CentOS忘记root用户密码,进入单用户模式修改密码
- Xen 和 KVM 下如何关闭 virbr0
- 2015-10-06 认识jQuery1
- 【PLSQL】如何对BLOB字段进行like操作
- CSS3选择器
- checkbox 全选和反选
- UVa 1208 - Oreon
- 快递100接口解析与Volley自定义XML解析
- 最全的安卓镜像和最全的android-studio
- 【编译原理】:简易的词法分析器
- 用Java语言实现单向链表的方法
- Arduino IDE的#ifdef bug