IRQL的理解和认识
介绍:
中断请求(IRQ,Interrupt Request)一般有两种,一种是外部中断,也就是硬件产生的中断(例如:键盘中断、打印机中断、定时器中断):另一种是由软件指令 int n 产生的中断(例如:INT 3 断点中断、INT 1 单步中断)。后来 Windows 将中断进行的扩展,提出一个中断请求级(IQRL)的概念。其中规定了 32个中断请求级别,分别是 0-2 级别为软件中断,3-31 为硬件中断。
Windows IRQL的宏定义
#define PASSIVE_LEVEL 0 // Passive release level
#define LOW_LEVEL 0 // Lowest interrupt level
#define APC_LEVEL 1 // APC interrupt level
#define DISPATCH_LEVEL 2 // Dispatcher level
#define CMCI_LEVEL 5 // CMCI handler level
#define PROFILE_LEVEL 27 // timer used for profiling.
#define CLOCK1_LEVEL 28 // Interval clock 1 level - Not used on x86
#define CLOCK2_LEVEL 28 // Interval clock 2 level
#define IPI_LEVEL 29 // Interprocessor interrupt level
#define POWER_LEVEL 30 // Power failure level
#define HIGH_LEVEL 31 // Highest interrupt level
#define CLOCK_LEVEL (CLOCK2_LEVEL)
对于驱动编程,我们通常只会接触到 0 到 2 的 IRQL 以及 DIRQL。
PASSIVE_LEVEL 0:
IRQL最低级别,没有被屏蔽的中断(无法屏蔽其他中断),在这个级别上,线程执行用户模式,可以访问分页内存。用户模式的代码是运行在最低级别的PASSIVE_LEVEL中,驱动程序的DriverEntry函数,派遣函数、AddDevice函数一般运行在PASSIVE_LEVEL中(驱动程序的StartIO和DPC函数运行在DISPATCH_LEVEL中),它们在必要的时候可以申请进入
DISPATCH_LEVEL级别(为了保证当前例程不会被CPU切换到其他地方),使用内核函数KeGetCurrentIrql()可以知道系统的当前IRQL。
APC_LEVEL 1 :
在这个级别上,只有处于APC级别的中断被屏蔽,可以访问分页内存。当有APC发生时,处理器提升到APC级别,这样,就屏蔽掉其他的APC,为了和APC执行一些同步,驱动程序可以手动提升到这个级别。APC级别仅仅比PASSIVE_LEVEL高,这也是在一个线程中插入一个APC可以打断该线程(如果该线程运行在PASSIVE_LEVEL上)运行的原因。
DISPATCH_LEVEL 2:
DISPATCH_LEVEL是一个重要的区分点,它代表了线程调度器正在运行。一个处理器运行运行在IRQL上,代表他可能正在做两件事之一:正在进行线程调度;正在处理硬件中断的后半部(不那么重要的部分),这个被称为DPC(Deferred Procedure Call:延迟调用)。这个级别,DPC(延迟过程)和更低的中断被屏蔽,不能访问分页内存,所有的被访问的内存不能分页。因为只能处理非分页内存,所有在这个级别上,能够访问的Api大大减少。
Windows负责线程调度的组件运行在DISPATCH_LEVEL级别,当前线程运行完时间片时,操
作系统自动从PASSIVE_LEVEL提升到DISPATCH_LEVEL级别,从而可以使得线程调度组件可以
调度其他线程。当线程切换完成后,操作系统又从DISPATCH_LEVEL级别恢复到
PASSIVE_LEVEL级别。
线程优先级和 IRQL
线程优先级是指线程是否有更多的机会运行在CPU上,线程优先级高的线程有更多的机会被内核调用。ReadFile内部创建IRP_MJ_READ,然后这个IRP被传递到驱动程序的派遣函数中,这时派遣函数运行于ReadFile所在的线程中,或者说ReadFile和派遣函数位于同一个线程的上下文中。
线程运行在PASSIVE_LEVEL级别,这个时候OS随时可能将当前线程切换到别的线程中去。但是将IRQL提升到DISPATCH_LEVEL级别时,这时则不会出现线程切换。这是一种很常用的处理机制,但是这种方法只能使用于单CPU的系统,对于多Cpu的系统,需要采用别的同步处理机制。
IRQL 与分页内存
在使用内存分页时,可能会导致页故障。因为分页内存随时可能从物理内存交换到磁盘文件中。读取不在物理内存中的分页时,会引发一个页故障,从而执行这个异常的处理函数。异常处理函数会重新将磁盘文件的内容交换到物理内存中。页故障允许在PASSIVE_LEVEL级别的程序中,但如果在DISPATCH_LEVEL或者更高级别IRQL的程序中会带来系统崩溃。
对于等于或者高于DISPATCH_LEVEL级别的程序不能使用分页内存,必须使用非分页内存。驱动程序的StartIO全程、DPC例程、中断服务例程都运行在DISPATCH_LEVEL或者更高的IRQL,因为这些例程不能使用分页内存,否则会导致系统崩溃。
当 IRQL< DISPATCH_LEVEL v1 内存申请成功
当 IRQL 提升到 DISPATCH_LEVEL 申请分页内存,导致系统蓝屏
错误码:IRQL_NOT_LESS_OR_EQUAL
蓝屏原因:缺页中断机制是运行在 DISPATCH_LEVEL 级别下的,和当前代码处于一个级别,当代码访问到一个内存页在换页文件的时候,缺页机制无法打断当前代码的运行,从而无法进行页交换,导致访问到了一个错误的内存地址,进而蓝屏。
IRQL 的提升和降低
有些时候驱动程序中需要提升IRQL级别,在运行一段时间后,再将降回原来的IRQL级别,这样做的目的一般是基于同步处理的需要。(当提升IRQL后,不允许低于当前IRQL的活动相互抢先,从而防止了潜在的冲突,这只适用于单Cpu的系统。当是多Cpu系统时,需要采用其他的同步机制,例如事件,自旋锁等等)首先驱动程序需要知道当前状态IRQL级别,可以通过KeGetCurrentIrql内核函数获取当前的IRQL级别。
然后驱动程序使用内核函数KeRaiseIrql将IRQL提高。KeRaiseIrql需要两个参数,第一个数是提升后的IRQL级别,第二个参数是保存提升前的IRQL级别。最后,驱动程序某个时刻需要将IRQL恢复到以前的IRQL级别,驱动程序可以调用
KeLowerIrql内核函数。
下面代码演示了驱动中如何提升和降低IRQL的代码
VOID RaiseIRQL_Test()
{
KIRQL oldirql;
// 确保当前IRQL等于或小于DISPATCH_LEVEL
ASSERT(KeGetCurrentIrql() <= DIPATCH_LEVEL);
// 提升IRQL到DISPATCH_LEVEL,并将先前的IRQL保存起来
KeRaiseIrql(DISPATCH_LEVEL, &oldirql);
//...
// 恢复到先前的IRQL
KeLowerIrql(oldirql);
}
IRQL的理解和认识相关推荐
- IRQL深入解析(3)--与IRQ比较
IRQ:Interrupt ReQuest,中断请求,当中断发生后,发生中断的设备通过它使用的中断请求信号线象中断控制器报告中断.CPU可以通过IRQ号来识别中断. IRQL:In ...
- 谈谈对APC的一点理解
谈谈对APC的一点理解 异步过程调用(APCs) 是NT异步处理体系结构中的一个基础部分,理解了它,对于了解NT怎样操作和执行几个核心的系统操作很有帮助. 1) APCs允许用户程序和系统元件在一个进 ...
- 【转】通过IRQL看NT内核
linux强调的是进程自主性,windows则是对象自主性,其中线程本身也是一个对象,进程也是,所以一个进程可以操作另外一个进程的地址空间也就不足 为奇了,windows的通信实际上是对象间通信,而l ...
- 理解和使用NT驱动程序的执行上下文
理解Windows NT驱动程序最重要的概念之一就是驱动程序运行时所处的"执行上下文".理解并小心地应用这个概念可以帮助你构建更快.更高效的驱动程序. NT标准内核模式驱动程序编程 ...
- 实时系统RTX之理解一
文献来源:http://wzhyblog.yo2.cn/articles/%e5%ae%9e%e6%97%b6%e7%b3%bb%e7%bb%9frtx%e5%ae%98%e6%96%b9%e6%96 ...
- 基于Minifilter框架的文件过滤驱动理解
概述 Minifilter即File System Minifilter Drivers,是Windows为了简化第三方开发人员开发文件过滤驱动而提供的一套框架,这个框架依赖于一个称之为Filter ...
- 通用解题法——回溯算法(理解+练习)
积累算法经验,积累解题方法--回溯算法,你必须要掌握的解题方法! 什么是回溯算法呢? 回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就&quo ...
- stream流对象的理解及使用
我的理解:用stream流式处理数据,将数据用一个一个方法去 . (点,即调用) 得到新的数据结果,可以一步达成. 有多种方式生成 Stream Source: 从 Collection 和数组 Co ...
- Linux shell 学习笔记(11)— 理解输入和输出(标准输入、输出、错误以及临时重定向和永久重定向)
1. 理解输入和输出 1.1 标准文件描述符 Linux 系统将每个对象当作文件处理.这包括输入和输出进程.Linux 用文件描述符(file descriptor)来标识每个文件对象.文件描述符是一 ...
- java局部变量全局变量,实例变量的理解
java局部变量全局变量,实例变量的理解 局部变量 可以理解为写在方法中的变量. public class Variable {//类变量static String name = "小明&q ...
最新文章
- 基于OpenCV的条形码区域分割
- 两句话动态修改table数据并提交到后台
- 使用DETR指令提取电子客票票面信息
- 人脸识别的python实现代码_手把手教你用1行代码实现人脸识别 --Python Face_recognition...
- Centos 5.3 Nginx+php+mysql配置 独立的 Subversion (SVN)服务器
- 《认知设计:提升学习体验的艺术》——差距在哪里
- Python:利用python代码编程实现将视频的avi格式转换为MP4格式
- 通达信板块监控指标_板块监控及使用方法指标详解 通达信板块监控
- 百度地图java批量获得经纬度_批量调用百度地图API获取地址经纬度坐标
- 医院信息化项目实施工作总结
- HTML基础笔记——head标签
- 【Excel VBA】银行卡信用卡卡号校验功能函数
- 王小锤学Java:retainAll函数那点儿事
- 解决idea不检查语法错误问题
- R中报错:Error :$ operator is invalid for atomic vectors
- Java开发者环境搭建
- 2021乐平四中高考成绩查询,喜报!乐平高考成绩出炉!2019再创佳绩!!
- linux中页缓冲和块缓冲之概念
- 我的世界java版钻石剑附魔_我的世界钻石剑附魔攻略
- 速卖通装修html自定义代码,Shopify基础建站教程,独立站装修主题代码设置