2.6 Windows驱动开发:使用IO与DPC定时器

本章将继续探索驱动开发中的基础部分,定时器在内核中同样很常用,在内核中定时器可以使用两种,即IO定时器,以及DPC定时器,一般来说IO定时器是DDK中提供的一种,该定时器可以为间隔为N秒做定时,但如果要实现毫秒级别间隔,微秒级别间隔,就需要用到DPC定时器,如果是秒级定时其两者基本上无任何差异,本章将简单介绍IO/DPC这两种定时器的使用技巧。

首先来看IO定时器是如何使用的,IO定时器在使用上需要调用IoInitializeTimer函数对定时器进行初始化,但需要注意的是此函数每个设备对象只能调用一次,当初始化完成后用户可调用IoStartTimer让这个定时器运行,相反的调用IoStopTimer则用于关闭定时。

// 初始化定时器
NTSTATUS IoInitializeTimer([in]           PDEVICE_OBJECT         DeviceObject,  // 设备对象[in]           PIO_TIMER_ROUTINE      TimerRoutine,  // 回调例程[in, optional] __drv_aliasesMem PVOID Context        // 回调例程参数
);
// 启动定时器
VOID IoStartTimer([in] PDEVICE_OBJECT DeviceObject             // 设备对象
);
// 关闭定时器
VOID IoStopTimer([in] PDEVICE_OBJECT DeviceObject             // 设备对象
);

这里我们最关心的其实是IoInitializeTimer函数中的第二个参数TimerRoutine该参数用于传递一个自定义回调函数地址,其次由于定时器需要依附于一个设备,所以我们还需要调用IoCreateDevice创建一个新设备来让定时器线程使用,实现定时器代码如下所示。

#include <ntifs.h>
#include <wdm.h>
#include <ntstrsafe.h>LONG count = 0;// 自定义定时器函数
VOID MyTimerProcess( __in struct _DEVICE_OBJECT *DeviceObject, __in_opt PVOID Context)
{InterlockedIncrement(&count);DbgPrint("定时器计数 = %d", count);
}VOID UnDriver(PDRIVER_OBJECT driver)
{// 关闭定时器IoStopTimer(driver->DeviceObject);// 删除设备IoDeleteDevice(driver->DeviceObject);DbgPrint(("Uninstall Driver Is OK \n"));
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark \n");NTSTATUS status = STATUS_UNSUCCESSFUL;// 定义设备名以及定时器UNICODE_STRING dev_name = RTL_CONSTANT_STRING(L"");PDEVICE_OBJECT dev;status = IoCreateDevice(Driver, 0, &dev_name, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &dev);if (!NT_SUCCESS(status)){return STATUS_UNSUCCESSFUL;}else{// 初始化定时器并开启IoInitializeTimer(dev, MyTimerProcess, NULL);IoStartTimer(dev);}Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

编译并运行这段代码,那么系统会每隔1秒执行一次MyTimerProcess这个自定义函数。

那么如何让其每隔三秒执行一次呢,其实很简单,通过InterlockedDecrement函数实现递减(每次调用递减1)当计数器变为0时InterlockedCompareExchange会让其继续变为3,以此循环即可完成三秒输出一次的效果。

LONG count = 3;// 自定义定时器函数
VOID MyTimerProcess(__in struct _DEVICE_OBJECT *DeviceObject, __in_opt PVOID Context)
{// 递减计数InterlockedDecrement(&count);// 当计数减到0之后继续变为3LONG preCount = InterlockedCompareExchange(&count, 3, 0);//每隔3秒计数器一个循环输出如下信息if (preCount == 0){DbgPrint("[LyShark] 三秒过去了 \n");}
}

程序运行后,你会看到如下输出效果;

相比于IO定时器来说,DPC定时器则更加灵活,其可对任意间隔时间进行定时,DPC定时器内部使用定时器对象KTIMER,当对定时器设定一个时间间隔后,每隔这段时间操作系统会将一个DPC例程插入DPC队列。当操作系统读取DPC队列时,对应的DPC例程会被执行,此处所说的DPC例程同样表示回调函数。

DPC定时器中我们所需要使用的函数声明部分如下所示;

// 初始化定时器对象 PKTIMER 指向调用方为其提供存储的计时器对象的指针
void KeInitializeTimer([out] PKTIMER Timer    // 定时器指针
);// 初始化DPC对象
void KeInitializeDpc([out]          __drv_aliasesMem PRKDPC Dpc,[in]           PKDEFERRED_ROUTINE      DeferredRoutine,[in, optional] __drv_aliasesMem PVOID  DeferredContext
);// 设置定时器
BOOLEAN KeSetTimer([in, out]      PKTIMER       Timer,     // 定时器对象的指针[in]           LARGE_INTEGER DueTime,   // 时间间隔[in, optional] PKDPC         Dpc        // DPC对象
);// 取消定时器
BOOLEAN KeCancelTimer([in, out] PKTIMER unnamedParam1         // 定时器指针
);

注意;在调用KeSetTimer后,只会触发一次DPC例程。如果想周期的触发DPC例程,需要在DPC例程被触发后,再次调用KeSetTimer函数,应用DPC定时代码如下所示。

#include <ntifs.h>
#include <wdm.h>
#include <ntstrsafe.h>LONG count = 0;
KTIMER g_ktimer;
KDPC g_kdpc;// 自定义定时器函数
VOID MyTimerProcess(__in struct _KDPC *Dpc,__in_opt PVOID DeferredContext,__in_opt PVOID SystemArgument1,__in_opt PVOID SystemArgument2)
{LARGE_INTEGER la_dutime = { 0 };la_dutime.QuadPart = 1000 * 1000 * -10;// 递增计数器InterlockedIncrement(&count);DbgPrint("DPC 定时执行 = %d", count);// 再次设置定时KeSetTimer(&g_ktimer, la_dutime, &g_kdpc);
}VOID UnDriver(PDRIVER_OBJECT driver)
{// 取消计数器KeCancelTimer(&g_ktimer);DbgPrint(("Uninstall Driver Is OK \n"));
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark \n");LARGE_INTEGER la_dutime = { 0 };// 每隔1秒执行一次la_dutime.QuadPart = 1000 * 1000 * -10;// 1.初始化定时器对象KeInitializeTimer(&g_ktimer);// 2.初始化DPC定时器KeInitializeDpc(&g_kdpc, MyTimerProcess, NULL);// 3.设置定时器,开始计时KeSetTimer(&g_ktimer, la_dutime, &g_kdpc);Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

编译并运行这段程序,会发现其运行后的定时效果与IO定时器并无太大区别,但是DPC可以控制更精细,通过la_dutime.QuadPart = 1000 * 1000 * -10毫秒级别都可被控制。

最后扩展一个知识点,如何得到系统的当前详细时间,获得系统时间。在内核里通过KeQuerySystemTime获取的系统时间是标准时间(GMT+0),转换成本地时间还需使用RtlTimeToTimeFields函数将其转换为TIME_FIELDS结构体格式。

#include <ntifs.h>
#include <wdm.h>
#include <ntstrsafe.h>/*typedef struct TIME_FIELDS{CSHORT Year;CSHORT Month;CSHORT Day;CSHORT Hour;CSHORT Minute;CSHORT Second;CSHORT Milliseconds;CSHORT Weekday;} TIME_FIELDS;
*/// 内核中获取时间
VOID MyGetCurrentTime()
{LARGE_INTEGER CurrentTime;LARGE_INTEGER LocalTime;TIME_FIELDS   TimeFiled;// 得到格林威治时间KeQuerySystemTime(&CurrentTime);// 转成本地时间ExSystemTimeToLocalTime(&CurrentTime, &LocalTime);// 转换为TIME_FIELDS格式RtlTimeToTimeFields(&LocalTime, &TimeFiled);DbgPrint("[时间与日期] %4d年%2d月%2d日 %2d时%2d分%2d秒",TimeFiled.Year, TimeFiled.Month, TimeFiled.Day,TimeFiled.Hour, TimeFiled.Minute, TimeFiled.Second);
}VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint(("Uninstall Driver Is OK \n"));
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{MyGetCurrentTime();DbgPrint("hello lyshark \n");Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

运行后即可在内核中得到当前系统的具体时间;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/142811.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RedCap推动5G规模应用,紫光展锐赋能产业高质量发展

5G R17 RedCap作为面向中高速物联网场景的关键技术和解决方案&#xff0c;可以大幅降低终端的复杂度、成本和功耗。在当前国内5G应用规模化发展关键时期&#xff0c;5G R17 RedCap拥有广大的市场潜力与广泛的应用场景&#xff0c;将有助于推动5G规模应用、构建融通发展的5G生态…

【海德教育】建筑八大员中综合来看,以下几个岗位比较吃香

1.施工员 施工员刚入行工资不高、劳动强度大&#xff0c;但发展前景可观。 通过考建造师等执业资格证&#xff0c;后期有望出任项目经理&#xff0c;拿到一万起步的月薪。 想要晋升项目经理、包工头等&#xff0c;对于社交能力和个人吃苦能力要求比较高&#xff0c;比较适合男孩…

7、线性数据结构-切片

切片slice 容器容量可变&#xff0c;所以长度不能定死长度可变&#xff0c;元素个数可变底层必须依赖数组&#xff0c;可以理解它依赖于顺序表&#xff0c;表现也像个可变容量和长度顺序表引用类型&#xff0c;和值类型有区别 定义切片 var s1 []int //长度、容量为0的切片&…

11月14日,每日信息差

今天是2023年11月14日&#xff0c;以下是为您准备的11条信息差 第一、中国航司首开“上海-开罗”往返直飞航线&#xff0c;12月11日首航 第二、中核集团&#xff1a;我国首次获得丰度99%以上的镱176同位素 第三、微软与谷歌将不会挑战欧盟的数据迁移法规&#xff0c;该法律将…

ProEasy机器人:运动+通讯相关说明

----------------机械手运动------- --常用指令&#xff1a;MovP弧线运动、MovL直线运动 MovP(1) --弧线轨迹运动到一号点 MovP(2) --弧线轨迹运动到二号点 MovL(1) --直线轨迹运动到一号点 MovL(2) --直线轨迹运…

LeetCode——链表(Java)

链表 简介[简单] 203. 移除链表元素[中等] 707. 设计链表[简单] 206. 反转链表[简单] 24. 两两交换链表中的节点[简单] 19. 删除链表的倒数第 N 个结点 简介 记录一下自己刷题的历程以及代码。写题过程中参考了 代码随想录。会附上一些个人的思路&#xff0c;如果有错误&#…

VB生成UTF-8的URL编码

最近在研发股票软件&#xff0c;涉及到URL字符串的处理。贴出以下代码。 Function GBtoUTF8(szInput) Dim wch, uch, szRet Dim x Dim nAsc, nAsc2, nAsc3 如果输入参数为空&#xff0c;则退出函数 If szInput "" Then GBtoUTF8 szInput Exit Function End If 开始…

前、后端程序员开发常用api接口

顺丰速运开放平台&#xff1a;可以查询顺丰速运的快递信息。京东物流开放平台&#xff1a;可以查询京东物流的快递信息。全国快递物流查询&#xff1a;1.提供包括申通、顺丰、圆通、韵达、中通、汇通等600快递公司在内的快递物流单号查询。2.与官网实时同步更新。3.自动识别快递…

【C++】join ()和detach ()函数详解和示例

简单的来说&#xff0c;join ()方法建立的线程具有阻碍作用&#xff0c;该线程不结束&#xff0c;另一些函数就无法运行。detach ()方法建立的线程&#xff0c;可以和另一些函数同时进行。下面以示例进行详细说明&#xff0c;以帮助大家理解和使用。 目录 join ()detach () jo…

PHP使用文件缓存实现html静态化

<?php // 动态生成的内容 $content "<html><body><h1>time:".date("Y-m-d H:i:s")."</h1></body></html>"; // 静态文件保存路径和文件名 $staticFilePath "file.html"; if(file_exists($s…

Ladybug 全景相机, 360°球形成像,带来全方位的视觉体验

360无死角全景照片总能给人带来强烈的视觉震撼&#xff0c;有着大片的既视感。那怎么才能拍出360球形照片呢&#xff1f;它的拍摄原理是通过图片某个点位为中心将图片其他部位螺旋式、旋转式处理&#xff0c;从而达到沉浸式体验的效果。俗话说“工欲善其事&#xff0c;必先利其…

java实现计数排序

图解 计数排序是一种线性时间复杂度的排序算法&#xff0c;它不基于比较排序&#xff0c;而是根据待排序序列中元素的值来进行排序。 具体的过程如下&#xff1a; 统计序列中每个元素出现的个数&#xff0c;得到一个计数数组count。其中&#xff0c;count[i]表示待排序序列中值…

pandas读写json的知识点

pandas对象可以直接转换为json&#xff0c;使用to_json即可。里面的orient参数很重要&#xff0c;可选值为columns,index,records,values,split,table A B C x 1 4 7 y 2 5 8 z 3 6 9 In [236]: dfjo.to_json(orient"columns") Out[236]: {"A":{"x&qu…

Centos 7rc.local脚本命令开机不执行及指定用户启动的方法

1.开机不启动 在实际生产场景中&#xff0c;我们喜欢在安装了一些软件服务后&#xff0c;将软件设置为开机自启动 配置在/etc/rc.local文件中。直接将软件服务的启动命令写在rc.local文件 注意&#xff1a;编辑完rc.local文件后&#xff0c;一定要给rc.local文件执行权限&#…

exit与return的区别 exit(1)、exit(-1)和exit(0)区别

目录 exit(); 是整个程序的结束。 exit(1)、exit(-1)和exit(0)区别 return();是某个函数的结束&#xff0c;并返回结果。 exit和_exit exit(); 是整个程序的结束。 exit&#xff08;0&#xff09;&#xff1a;正常运行程序并退出程序&#xff1b; exit&#xff08;1&am…

AIGC:使用bert_vits2实现栩栩如生的个性化语音克隆

1 VITS2模型 1.1 摘要 单阶段文本到语音模型最近被积极研究&#xff0c;其结果优于两阶段管道系统。以往的单阶段模型虽然取得了较大的进展&#xff0c;但在间歇性非自然性、计算效率、对音素转换依赖性强等方面仍有改进的空间。本文提出VITS2&#xff0c;一种单阶段的文本到…

Xilinx Kintex7中端FPGA解码MIPI视频,基于MIPI CSI-2 RX Subsystem架构实现,提供工程源码和技术支持

目录 1、前言免责声明 2、我这里已有的 MIPI 编解码方案3、本 MIPI CSI2 模块性能及其优缺点4、详细设计方案设计原理框图OV5640及其配置权电阻硬件方案MIPI CSI-2 RX SubsystemSensor Demosaic图像格式转换Gammer LUT伽马校正VDMA图像缓存AXI4-Stream toVideo OutHDMI输出 5、…

Java安全架构 JCA、JCE、JSSE、JAAS

Java语言拥有三大特征&#xff1a;平台无关性、网络移动性和安全性&#xff0c;而Java安全体系结构对这三大特征提供了强大的支持和保证&#xff0c; Java安全体系结构总共分为4个部分&#xff1a; &#xff08;1&#xff09;JCA&#xff08; Java Cryptography Architecture…

工具及方法 - 手机扫条码工具: SCANDIT APP

一般扫个链接使用微信扫一扫即可。扫具体条码&#xff0c;可以在微信里搜索小程序&#xff0c;打开也能扫&#xff0c;得到条码内容。 还有其他方式&#xff0c;比如使用淘宝、百度等APP也可以直接扫码条码&#xff0c;还能得到更多的信息。 使用百度的话&#xff0c;不扫条码…

【洛谷算法题】P5711-闰年判断【入门2分支结构】

&#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5711-闰年判断【入门2分支结构】&#x1f30f;题目描述&#x1f30f;输入格式&a…