APC 基本概念及APC注入的实现(Ring3 + Ring0)----概念介绍

来源:互联网 发布:线性时间选择c语言 编辑:程序博客网 时间:2024/06/14 18:11

APC 基本概念及APC注入的实现(Ring3 + Ring0)—-概念介绍


基本术语

  • 中断—-一个异步事件,可能在任何时候发生,与处理器当前正在执行的代码无任何关系。—-中断的产生主要有:I/O 设备、处理器始终,或者定时器,另外,终端可以被打开或者被禁止。

  • 异常是一个同步事件,它是一个特殊指令执行的结果,特点就是可重复与可预见性。在同样的条件下用同样的数据第二次运行一个程序可以重现原来的异常。比如:内存访问违例、特定的调试器指令,除零错误等。

  • 系统陷阱—即系统服务调用,内核把它也看作一种异常。

  • 陷阱(trap)—-“当异常或者中断发生时,处理器捕捉到一个执行线程,并且将控制权转移到操作系统中某一固定地址处”的一种机制。
  • 陷阱处理器—-指的是与某个特殊的中断或异常相关联的一个函数。在Windows 中,陷阱概念中的固定地址就是陷阱处理器。
  • 通用的系统服务陷阱处理器—-在内核将控制权转交给与该陷阱相关的其它函数之前或之后,由这些处理器做一些常规的处理任务—-比如,判断陷阱类型并做相应的转交。

中断分发

       中断分为硬件中断和软件中断。
       I/O 设备通过中断通知处理器,以请求自己需要的服务。
       软件中断主要包括DPC(分发或者延迟过程调用中断)和APC(一部过程调用中断)。
       

  • 中断陷阱处理器。
           内核安装了中断陷阱处理器来响应设备的中断。中断陷阱处理器或者将控制权转交给一个负责处理该中段的外部例程(ISR)(硬件),或者传递给一个响应该中断的内部内核例程(软件)。

硬件中断处理

  • 中断分发表(IDC,interrupt dispatch table)
           在系统引导的时候,Windows 会填充IDT,其中包含了指向内核中负责处理各个中断和异常的例程的指针。IDT 同时配置了中断和异常,比如0x30 对应时钟中断,0xe 对应的系统的页面错误处理器。
           每个处理器有单独的IDT,比如,在一个多处理器系统中,每个处理器都会接收时钟中断,所有的处理器都使用该中断来测量线程的时限,但是,只有一个处理器在响应该中断的时候更新系统时钟。

  • 硬件中断控制器
           外部I/O 中断进入到中断控制器的某一根线上。该控制器然后在一根线上中断处理器。一旦处理器被中断了,它询问控制器以获得此中断请求(IRQ,interrupt request)。中断控制器将IRQ转换为一个中断号(即IDT 索引),然后将控制权传递给恰当的中断分发例程。

X86 中断控制器

  • PIC(可编程中断控制器)与APIC(高级可编程中断控制器)
           APC(15根中断线)APIC(256根中断线),当前大部分X86-PC 依赖于APIC,同时兼容APC。APC 由几个部件构成:
    • 一个专门接收设备中断的I/O APIC
    • 本地APIC
             本地APIC 在一条私有的APIC 总线上接收来自I/O APIC 的中断。
    • 与i8259A 兼容的中断控制器,负责将APIC 传入转换成与PIC 等价的信号。
             I/O APIC 负责实现中断传送算法—–这些算法由软件来选择(Windows 硬件抽象层或者HAL)—-既可以平衡各个处理器之间的设备中断负载,也可以设法利用位置特性,将设备中断递交给刚刚承担了前一个同样类型中断的同一个处理器。

X64 中断控制器

* X64 版本Windows 必须运行在支持APIC 的系统上*

软件中断请求级别(IRQL)

  • 虽然中断控制器已经实现了一层中断优先级,Windows 强迫使用它自己的中断优先级方案,称为中断请求级别(IRQL,interrupt request level)。
           X86中,内核内部使用0~30 代表IRQL。
           X64 和 IA64 系统中,采用从0~15代表IRQL。
  • 数值越大,中断的优先级越高。
  • HAL 将硬件中断号映射为IRQL。
  • 中断的优先级与线程调度优先级的关系
           中断是按照优先级处理的,高优先级的中断会抢占低优先级中断的执行权。与线程调度优先级比起来,调用优先级是一个线程的属性,而IRQL 则是中断源(键盘、鼠标)的属性,而且,每个处理器有一个可随着操作系统代码的执行而变化的IRQL 设置。每个处理器的IRQL 设置决定了该处理器可以接收哪些中断,所谓的屏蔽中断,就产生在线程IRQL 高于硬件中断源的时候。只有内核模式线程有权限修改线程IRQL,用户模式线程一直执行在被动级别。
  • 延迟IRQL
           为了避免访问PIC 这样一个比较慢的动作,使用了PIC 的HAL,实现了延迟IRQL。当IRQL 被提升时,HAL在内部记录下新的IRQL,如果一个更低优先级的中断随后发生,HAL 将中断屏蔽值设置成对于第一个中断较为正确的值,并且将更低优先级的中断延迟至IRQL 被降低下来为止。如果当IRQL 被提升时没有更低优先级的中断发生,HAL 不需要修改PIC。
  • 中断发生的时候,陷阱处理器将该处理器的IRQL提升至中断源的IRQL,屏蔽(同一处理器的)底层中断、以及同级的中断。系统所有组件及内核都视图让IRQL 保持在被动级别上,以及时响应硬件中断。

将中断映射到IRQL

  • windows 中,总线型驱动程序的设备驱动程序确定它的总线上有哪些设备,以及哪些中断可以分配给某一个设备。总线型驱动程序将这些信息汇报给即插即用管理器,即插即用管理器在考虑了所有其它设备的可接受的中断分配方案后,确定为每个设备分配哪个中断。然后,它调用HAL 函数HalpGetSystemInterruptVector ,该函数将中断映射到对应的IRQL

预定义的IRQL

IRQL 作用 高级的IRQL 内核在KebugCheckEx 中停止了系统并屏蔽了所有中断的时候使用 电源失效 未被使用到过 处理期间的中断 用于向另一个处理器请求执行一个动作,比如将一个DISPATCH_LEVEL 中断排队到队列中以便为它调度到一个特定的线程来执行其任务、更新处理器的地址转换快查缓冲区(TLB)的高速缓存、系统关闭或者系统崩溃 时钟 主要用于系统的时钟,内核利用该中断级别来跟踪具体的时刻,以及为线程测量或分配CPU 时间 性能剖析 当内核的性能剖析功能被打开的时候使用 设备IRQL 被用来对设备中断进行优先级区分 DPC/APC 由内核和设备驱动驱动程序产生的软件中断 被动级别 是普通线程的运行时的设置,此时所有的中断都允许发生

       运行在DPC/Dispatch 级别或更高级别的代码,不能等待一个对象、只能访问非换页内存。—-此时线程进入等待,迫使调度器选择另一个线程来执行,由于调度器在Dispatch 级别上和它的数据结构同步,因此它不能被调用来执行重新调度的操作。

中断对象

  •        中断对象中包含了所有“供内核将一个设备的ISR 与一个特定级别的中断关联起来而需要”的信息,包括该ISR 的地址、该设备中断时所在的IRQL,以及内核中与该ISR 关联的IDT 项当一个中断对象被初始化的时候,少量的汇编代码被从一个中断处理模板KiInterruptTemplate 拷贝过来,保存在该对象中。当发生中断的时候,这些代码将被执行。
  • 中断代码
           上面讲到的中断代码,调用了实际的中断分发器,通常是内核的KiInterruptDispatch(一个中断向量-一个中断对象) 或 KiChainedDispatch(一个中断向量-多个中断对象) 例程,并将指向该中断对象的指针传递给它

       将一个ISR 与某个特定的中断级别关联起来,这一操作称为连接一个中断对象,而将一个ISR 与IDT 项断开关联,称为断开一个中断对象。这些操作是通过调用内核函数IoConnectInterrupt 和 IoDisconnectInterrupt 来完成的。
       使用中断对象来注册一个ISR ,可以避免设备驱动程序直接操纵中断硬件,也可以不必知道IDT 的内部细节。有助于创建可移植的设备驱动程序。

软件中断

可以激发软件中断的任务包括
- 激发线程分发
- 并非时间紧迫的中断处理
- 处理定时器到期
- 在特定线程的环境中异步执行一个过程;
- 支持异步I/O 操作

分发或者延迟过程调用(DPC)中断

  • 分发中断的含义
           当IRQL 在 DPC/Dispatch 或更高的时候,禁止了软件中断和线程分发动作。当内核检测到应该进行分发的时候,请求一个DPC/Dispatch 级别的中断,但是,因为IRQL >= DPC/Dispatch ,处理器保留该中断,等内核完成了当前动作,它知道把IRQL 降低到DPC/Dispatch 一线,并且查看一下是否有分发中断正在等待处理。有的话,将IRQL 设置为 DPC/Dispatch,于是这些分发中断可以得到处理了。利用软件中断来激活线程分发器,这是一种将分发过程推迟到条件何时时才进行的方法。
  • 延迟过程调用的含义
           参照上面的思路,我们可以发现,DPC 赋予操作系统一种能力:产生一个中断并在内核模式下执行一个系统函数。前面说过,Windows—在设备驱动程序的配合下–企图将IRQL 保持在低于设备IRQL 的级别下。达到这一目的的一种方法是,让设备驱动程序ISR 执行最少最必要的工作来响应它们的设备、将易变的中间状态保存起来,将数据传输或者其它非时间紧迫的中断处理活动推迟到一个位于DPC/Dispatch IRQL 级别的DPC 上再执行。

  • DPC 对象
           每个处理器有一个DPC 队列,用于存储等待执行的DPC例程,DPC 有低中高三个优先级,可以将DPC 对象放到任意的处理器上。DPC 对象是一种内核控制对象,仅内核可见,其最重要的信息就是一个函数地址,在内核处理该DPC 时要调用该函数。

  • DPC 执行的两种方式
           处理器的IRQL将要从DPC/Dispatch 或者更高的IRQL 降低到某个更低的IRQL,内核确保IRQL 保持在DPC/Dispatch 级别,抽空DPC 队列,依次执行DPC 例程。

       产生DPC 中断的条件

DPC优先级 DPC被定为在ISR 的处理器上 DPC 被定位在另一个处理器上 低级 DPC 队列长度超过最大的DPC队列长度值,或者DPC请求率小于最小的DPC 请求率 DPC 队列长度超过最大的DPC 队列长度值,或者系统空闲 中级 总是激发 DPC 队列长度超过最大的DPC 队列长度值,或者系统空闲 高级 总是激发 总是激发(通过发送IPI)

异步过程调用(APC)中断

  • APC 提供了一种在特定的用户线程环境(从而也是特定进程的地址空间)中执行用户程序和系统代码的途径。(DPC 的执行环境是不确定的)

  • APC 对象
           APC 是由一个内核控制对象来描述的,称为APC 对象。APC 是线程相关的,当内核接收到请求,要将一个APC 排队时,它将该APC 插入到将来执行此APC 例程的那个线程的队列中。然后,内核请求一个APC 级别的软件中断,当该线程最终开始执行的时候,就会执行此APC。
           有两种类型的APC,内核模式和用户模式。

  • 内核模式APC
           内核模式APC 无需目标线程的干涉或者同意,就可以中断该线程并执行一个过程。内核模式的APC有两种类型:普通的和特殊的。将IRQL 提升到APC_LEVEL 或者调用KeEnterGuardedRegion 就可以禁止这两种类型的APC。KeEnterCriticalRegion 可以禁止普通APC。* 执行体使用内核模式的APC 来完成那些必须要在特定线程的地址空间中才能完成的操作系统任务*

  • 用户模式APC
           ReadFileEx 和 WriteFileEx 函数允许用户指定一个完成例程。当I/O 操作完成的时候该完成例程被调用。I/O 完成机制是通过在发起I/O 操作的线程里插入一个APC 来实现的。

  • 可警告的等待状态
           只有当一个线程处于可警告的等待状态,用户模式APC 才可以交付给该线程。一个线程可以通过两种方式进入该状态:
                  等待一个对象句柄并指定它的状态是可警告的(使用WaitForMultipleObjectsEx 函数)
                  直接测试一下是否有未处理的APC(使用SleepEx)。
           这两种情况下,如果有一个用户模式的APC 正在等待处理,内核中断该线程,将控制权传递给APC 例程,当APC 例程完成时恢复线程的运行。
           内核模式APC 运行在APC 级别上,不同的是,用户模式APC 运行在被动级别上。

       APC 的交付可能导致等待队列的重新排序。如果当一个APC 被交付时,线程正处于等待状态,则在APC 例程完成之后,此等待动作将被重新发起或被重新执行。如果该等待动作尚未被解析,则线程返回到等待状态,但现在,在它所等待的对象中,它位于等待列表的末尾。

原创粉丝点击