1. 同步


同步创建方式的意思是应用层必须要等待IRP被IoCompleteRequest后才会返回。其必须是同步的。即使底层设备对收到的IRP进行挂起处理,那也得无限等待下去。看一下下面的例子:

该例子主要是进行了以下步骤:

  1. 应用层程序发送了Read类型的IRP给中间设备
  2. 中间设备捕获IRP后完成该IRP并调用ZwReadFile重新创建了一个IRP给底层设备
  3. 底层设备挂起该IRP并设置DPC例程来处理该被挂起的IRP
  4. 底层设备DPC例程完成了IRP后返回

接下去看一下底层设备代码:

#include <ntddk.h>typedef struct _DEVICE_EXTENSION {PDEVICE_OBJECT pDevice;UNICODE_STRING ustrDeviceName;  //设备名称UNICODE_STRING ustrSymLinkName;   //符号链接名KDPC pollingDPC; // 存储DPC对象KTIMER pollingTimer;// 存储计时器对象PIRP currentPendingIRP;//记录当前挂起的IRP
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;VOID Unload(IN PDRIVER_OBJECT pDriverObject) {PDEVICE_OBJECT pNextObj;KdPrint(("DriverA: 进入Unload例程\n"));pNextObj = pDriverObject->DeviceObject;while (pNextObj) {PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pNextObj->DeviceExtension;UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;IoDeleteSymbolicLink(&pLinkName);IoDeleteDevice(pDevExt->pDevice);pNextObj = pNextObj->NextDevice;}KdPrint(("DriverA: 离开Unload例程\n"));
}#define MICROSECOND(s) (-10 * s)
#define MILLISECOND(s) (MICROSECOND(s) * 1000)
#define SECOND(s) (MILLISECOND(s) * 1000)void OnTimerDpc(PKDPC Dpc, PVOID Context, PVOID SystemArgument1, PVOID SystemArgument2) {KdPrint(("DriverA: 进入DPC例程\n"));PDEVICE_OBJECT pDevObj = (PDEVICE_OBJECT)Context;PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;PIRP currentPendingIrp = pDevExt->currentPendingIRP;currentPendingIrp->IoStatus.Status = STATUS_SUCCESS;currentPendingIrp->IoStatus.Information = 0;KdPrint(("完成了挂起的IRP: 0x%08X\n", currentPendingIrp));IoCompleteRequest(currentPendingIrp, IO_NO_INCREMENT);KdPrint(("DriverA: 离开DPC例程\n"));
}NTSTATUS CreateDevice(IN PDRIVER_OBJECT    pDriverObject) {NTSTATUS status;PDEVICE_OBJECT pDevObj;PDEVICE_EXTENSION pDevExt;//创建设备名称UNICODE_STRING devName;RtlInitUnicodeString(&devName, L"\\Device\\DevA");//创建设备status = IoCreateDevice(pDriverObject, sizeof(DEVICE_EXTENSION), &devName, FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevObj);if (!NT_SUCCESS(status)) {KdPrint(("IoCreateDevice: %08X\n", status));return status;}pDevObj->Flags |= DO_BUFFERED_IO;pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;pDevExt->pDevice = pDevObj;pDevExt->ustrDeviceName = devName;KeInitializeTimer(&pDevExt->pollingTimer);KeInitializeDpc(&pDevExt->pollingDPC, OnTimerDpc, (PVOID)pDevObj);UNICODE_STRING symLinkName;RtlInitUnicodeString(&symLinkName, L"\\??\\DevA");pDevExt->ustrSymLinkName = symLinkName;status = IoCreateSymbolicLink(&symLinkName, &devName);if (!NT_SUCCESS(status)) {KdPrint(("IoCreateSymbolicLink: %08X\n", status));IoDeleteDevice(pDevObj);return status;}return STATUS_SUCCESS;
}NTSTATUS DispatchRoutine(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) {NTSTATUS status = STATUS_SUCCESS;pIrp->IoStatus.Status = status;pIrp->IoStatus.Information = 0;IoCompleteRequest(pIrp, IO_NO_INCREMENT);return status;
}NTSTATUS ReadRoutine(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) {KdPrint(("DriverA: 进入了ReadRoutine\n"));PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;LARGE_INTEGER li = RtlConvertLongToLargeInteger(-30 * 1000 * 1000);pDevExt->currentPendingIRP = pIrp; // 保存IRPIoMarkIrpPending(pIrp); // 挂起IRPKeSetTimer(&pDevExt->pollingTimer, li, &pDevExt->pollingDPC); // 设置了DPC例程3秒后启动KdPrint(("DriverA: 离开了ReadRoutine\n"));return(STATUS_PENDING);
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath) {KdPrint(("DriverA: 进入DriverEntry\n"));pDriverObject->DriverUnload = Unload;for (int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; ++i) pDriverObject->MajorFunction[i] = DispatchRoutine;pDriverObject->MajorFunction[IRP_MJ_READ] = ReadRoutine;NTSTATUS status = CreateDevice(pDriverObject);if (!NT_SUCCESS(status)) return(status);KdPrint(("DriverA: 离开DriverEntry\n"));return(STATUS_SUCCESS);
}

来看一下中间设备代码:

#include <ntddk.h>typedef struct _DEVICE_EXTENSION {PDEVICE_OBJECT pDevice;UNICODE_STRING ustrDeviceName;  //设备名称UNICODE_STRING ustrSymLinkName;   //符号链接名
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;NTSTATUS DispatchRoutine(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) {NTSTATUS status = STATUS_SUCCESS;pIrp->IoStatus.Status = status;pIrp->IoStatus.Information = 0;IoCompleteRequest(pIrp, IO_NO_INCREMENT);return status;
}VOID Unload(IN PDRIVER_OBJECT pDriverObject) {PDEVICE_OBJECT pNextObj;KdPrint(("DriverB: 进入Unload例程\n"));pNextObj = pDriverObject->DeviceObject;while (pNextObj) {PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pNextObj->DeviceExtension;UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;IoDeleteSymbolicLink(&pLinkName);IoDeleteDevice(pDevExt->pDevice);pNextObj = pNextObj->NextDevice;}KdPrint(("DriverB: 离开Unload例程\n"));
}NTSTATUS CreateDevice(IN PDRIVER_OBJECT    pDriverObject) {NTSTATUS ntStatus;PDEVICE_OBJECT pDevObj;PDEVICE_EXTENSION pDevExt;UNICODE_STRING symLinkName;UNICODE_STRING devName;RtlInitUnicodeString(&devName, L"\\Device\\DevB");ntStatus = IoCreateDevice(pDriverObject, sizeof(DEVICE_EXTENSION), &devName, FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevObj);if (!NT_SUCCESS(ntStatus))return ntStatus;pDevObj->Flags |= DO_BUFFERED_IO;pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;pDevExt->pDevice = pDevObj;pDevExt->ustrDeviceName = devName;RtlInitUnicodeString(&symLinkName, L"\\??\\DevB");pDevExt->ustrSymLinkName = symLinkName;NTSTATUS status = IoCreateSymbolicLink(&symLinkName, &devName);if (!NT_SUCCESS(status)) {IoDeleteDevice(pDevObj);return status;}return STATUS_SUCCESS;
}NTSTATUS ReadRoutine(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) {KdPrint(("DriverB: 进入ReadRoutine\n"));PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;OBJECT_ATTRIBUTES obj;UNICODE_STRING ustrDevName;HANDLE hDevA;NTSTATUS status = STATUS_SUCCESS;IO_STATUS_BLOCK IoStatus;RtlInitUnicodeString(&ustrDevName, L"\\Device\\DevA"); // 获取DevA的设备名称InitializeObjectAttributes(&obj, &ustrDevName, OBJ_CASE_INSENSITIVE, NULL, NULL);status = ZwCreateFile(&hDevA, FILE_ALL_ACCESS | SYNCHRONIZE, &obj, &IoStatus, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);if (!NT_SUCCESS(status)) {KdPrint(("打开设备A失败\n"));return(status);}// 创建一个新的Read类型的IRP发送给底层驱动status = ZwReadFile(hDevA, NULL, NULL, NULL, &IoStatus, NULL, 0, NULL, 0);if (!NT_SUCCESS(status)) {KdPrint(("读取设备A失败\n"));return(status);}ZwClose(hDevA);// 应用层发来的IRP被完成KdPrint(("应用层发来的IRP: %08X\n"));pIrp->IoStatus.Status = STATUS_SUCCESS;pIrp->IoStatus.Information = 0;IoCompleteRequest(pIrp, IO_NO_INCREMENT);KdPrint(("DriverB: 离开ReadRoutine\n"));return(status);
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath) {KdPrint(("DriverB: 进入DriverEntry\n"));pDriverObject->DriverUnload = Unload;for (int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; ++i)pDriverObject->MajorFunction[i] = DispatchRoutine;pDriverObject->MajorFunction[IRP_MJ_READ] = ReadRoutine;NTSTATUS status = CreateDevice(pDriverObject);if (!NT_SUCCESS(status))return(status);KdPrint(("DriverB: 离开DriverEntry\n"));return(STATUS_SUCCESS);
}

来看下应用层代码:

#include <windows.h>
#include <stdio.h>int main() {HANDLE hDevice = NULL;hDevice = CreateFile("\\\\.\\DevB", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);if (INVALID_HANDLE_VALUE == hDevice) {printf("打开设备失败! %d\n", GetLastError());return(-1);}DWORD dwRet; ReadFile(hDevice, NULL, 0, &dwRet, NULL);CloseHandle(hDevice);return(0);
}

程序运行结果:

可以看得出来两个IRP是不一样的,因为中间驱动做的并不是直接转发IRP,而是完成应用层IRP后再重新创建一个新的IRP发送到底层设备

值得注意的是ZwCreateFile的参数:

ZwCreateFile(&hDevA, FILE_ALL_ACCESS | SYNCHRONIZE, &obj, &IoStatus, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);

第二个参数要加上SYNCHRONIZE表示同步,倒数第三个参数必须指定为: FILE_SYNCHRONOUS_IO_NONALERT或者FILE_SYNCHRONOUS_IO_ALERT

2. 异步创建


第一种异步调用的方法是通过同步对象并设置APC例程成来达到的。来看一下具体的实现步骤

  1. 应用层发来Read类型的IRP请求到中间设备
  2. 中间设备通过ZwReadFile发送Read类型的异步IRP请求到底层设备,并对该新创建的IRP设置APC例程,并传入一个event同步对象
  3. 底层设备设置了等待3秒后开始的DPC例程,挂起IRP后返回STATUS_PENDING
  4. 中间设备收到了STATUS_PENDING后阻塞在KeWaitForSingleObject上
  5. 3秒后底层设备的DPC例程被触发,并在其中通过调用IoCompleteRoutine完成了中间设备传下来的IRP请求,中间设备的APC例程被触发
  6. 中间设备的APC例程中触发了同步事件对象后返回
  7. KeWaitForSingleObject的阻塞状态被解除并完成了应用层发来的IRP

其中最关键的因素在于底层设备ZwReadFile设置APC例程,APC例程触发同步事件。底层设备返回STATUS_PENDING并且DPC例程完成中间设备发来的IRP请求

由于底层设备和应用层的代码没有任何改变,这里仅仅给出中间设备代码修改过的代码,来看一下实现代码:

VOID ApcRoutine (
_In_ PVOID ApcContext,
_In_ PIO_STATUS_BLOCK IoStatusBlock,
_In_ ULONG Reserved
) {KdPrint(("DriverB: 进入了APC例程\n"));// 触发同步事件KeSetEvent((PKEVENT)ApcContext, IO_NO_INCREMENT, FALSE);KdPrint(("DriverB: 离开了APC例程\n"));
}NTSTATUS ReadRoutine(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) {KdPrint(("DriverB: 进入ReadRoutine\n"));PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;OBJECT_ATTRIBUTES obj;UNICODE_STRING ustrDevName;HANDLE hDevA;NTSTATUS status = STATUS_SUCCESS;IO_STATUS_BLOCK IoStatus;KEVENT event;RtlInitUnicodeString(&ustrDevName, L"\\Device\\DevA"); // 获取DevA的设备名称InitializeObjectAttributes(&obj, &ustrDevName, OBJ_CASE_INSENSITIVE, NULL, NULL);KeInitializeEvent(&event, NotificationEvent, FALSE); // 初始化事件// 没有FILE_SYNCHRONOUS_IO_NONALERT标志status = ZwCreateFile(&hDevA, FILE_ALL_ACCESS, &obj, &IoStatus, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN_IF, 0, NULL, 0);if (!NT_SUCCESS(status)) {KdPrint(("打开设备A失败%08X\n", status));return(status);}LARGE_INTEGER li = RtlConvertLongToLargeInteger(0);// 创建一个新的Read类型的IRP发送给底层驱动, 同时设置APC例程status = ZwReadFile(hDevA, NULL, ApcRoutine, &event, &IoStatus, NULL, 0, &li, 0);if (!NT_SUCCESS(status)) {KdPrint(("读取设备A失败%08X\n", status));return(status);}if (status == STATUS_PENDING) {KdPrint(("底层IRP返回了STATUS_PENDING, 等待底层IRP被完成...\n"));KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);}ZwClose(hDevA);// 应用层发来的IRP被完成KdPrint(("应用层发来的IRP: %08X\n"));pIrp->IoStatus.Status = STATUS_SUCCESS;pIrp->IoStatus.Information = 0;IoCompleteRequest(pIrp, IO_NO_INCREMENT);KdPrint(("DriverB: 离开ReadRoutine\n"));return(status);
}

来看一下结果:

我认为最值得注意的是两个函数, ZwCreateFile和ZwReadFile:

status = ZwCreateFile(&hDevA, FILE_ALL_ACCESS, &obj, &IoStatus, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN_IF, 0, NULL, 0);
ZwReadFile(hDevA, NULL, ApcRoutine, &event, &IoStatus, NULL, 0, &li, 0);

与同步方式不同的是

  • ZwCreateFile不需要SYNCHRONIZE属性。代表异步。倒数第三个参数不需要FILE_SYNCHRONOUS_IO_NONALERT或FILE_SYNCHRONOUS_IO_ALERT
  • ZwReadFile必须要填入倒数第二个参数,否则会失败

(完)

驱动中同步与异步发送IRP相关推荐

  1. java中同步和异步简介及应用场景

    转自: java中同步和异步简介及应用场景 同步: 同步指两个或两个以上随时间变化的量在变化过程中保持一定的相对关系. 本意是指某人或某事同时进行:同时产生,比如音画同步.动作同步等. 同步,指对在一 ...

  2. 杂谈——Java中同步与异步有什么区别

    大家在使用手机的时候,比如小米手机,自带云空间,可以将数据同步到云空间上:很多文档app也有同步的功能.而我们牛气冲冲的Java自然也有同步与异步,不过Java中的同步与异步似乎与我们平常认知中的同步 ...

  3. IO中同步、异步与阻塞、非阻塞的区别

    一.同步与异步 同步/异步, 它们是消息的通知机制 1. 概念解释 A. 同步 所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回. 按照这个定义,其实绝大多数函数都是同步调用(例 ...

  4. JS中同步和异步问题及其区别

    1.同步和异步 首先,我们浏览器常用的是JS语言,但其实相比于JAVA来说,JS其实是单线程语言,JavaScript在同一个时间只能做一件事,单线程意味着,如果在同个时间有多个任务的话,这些任务就需 ...

  5. Kafka生产者——消息发送流程,同步、异步发送API

    生产者消息发送流程 发送原理 Kafka的Producer发送消息采用的是异步发送的方式. 在消息发送的过程中,涉及到了两个线程:main线程和Sender线程,以及一个线程共享变量:RecordAc ...

  6. java同步和异步的区别_java中同步与异步区别是什么

    一.同步与异步概念:(推荐:java视频教程) 1.同步:所有的操作都做完,才返回给用户.这样用户在线等待的时间太长,给用户一种卡死了的感觉(就是系统迁移中,点击了迁移,界面就不动了,但是程序还在执行 ...

  7. ASP.NET MVC中同步与异步

    1.MvcHandler总是调用BeginProcessRequest/EndProcessRequest方法以异步的方式来处理请求 2.Controller分别实现了IController和IAsy ...

  8. 网络编程中同步与异步,IO阻塞与非阻塞总结

    IO操作分两个阶段 第1个阶段:等待数据准备好(从外部设备磁盘或网络读到内核缓冲区): 第2个阶段:采用系统调用(内核进程),操作系统内核将数据从内核缓冲区读到用户空间. 第1阶段花费的时间远远大于第 ...

  9. 一文了解JAVA中同步、异步、阻塞和非阻塞

最新文章

  1. JS原生选项卡 – 幻灯片效果
  2. 云数据管理公“云信达”获东方富海数千万A轮投资
  3. [error] OpenEvent(Global\ngx_stop_25184) failed (2: The system cannot find the file specified)
  4. 警告:‘xxxx’ 将随后被初始化
  5. zip (ICSharpCode.SharpZipLib.dll文件需要下载)
  6. 如何制作自动更新程序?
  7. c语言 指针6行7列矩阵的转置,指针第一次练习
  8. 京东方OLED屏幕无缘iPhone 12首批供货,因测试未通过
  9. 60-100-020-使用-MySQL 的Show Profile命令
  10. Mysql-2-数据库基础
  11. LeetCode 71. Simplify Path
  12. 闪迪内存卡软件测试,存储卡不稳定?我们用微波炉测试 结果震惊了!
  13. Genymotion启动报错:VT-x/AMD-V硬件加速在您的系统中不可用
  14. 国内遥感卫星资源综述
  15. 字符型变量ch的值为英文字母 的c语言表达式
  16. 程序员理想中的工作环境是什么样的?
  17. 作网站需要服务器吗,如何制作网站服务器
  18. 随机游走模型 matlab,随机游走的matlab实现
  19. 计算机二级考风考纪主题班会,2021年我国计算机二级考试基础概述.doc
  20. 使用在线UML制作工具Freedgo Design设计uml例子

热门文章

  1. 【面试题】一个Http请求的流程
  2. TKeed源码解析之URI解析
  3. 用c语言实现作曲与播放教程~,原创哦~
  4. POI解决读入Excel内存溢出
  5. 【优化系列】汇编优化技术(九):WebAssembly(wasm)平台SIMD优化
  6. Tommy Hilfiger 宣布,F1世界冠军Lewis Hamilton担任TOMMY HILFIGER全球男装代言人
  7. 小米手机刷机失败之小米La¥%¥Ji
  8. 论文笔记 Stochastic Gradient Hamiltonian Monte Carlo (SGHMC)
  9. 来谈一谈专注力的真相
  10. php 除法,php中如何除法取整