zoukankan      html  css  js  c++  java
  • 译:《深入解析WINDOWS VISTA APC》——PART 1

    作者:Enrico Martignetti
    译者:zhouws/kkindof qq5771067
    原文:http://www.opening-windows.com/download/apcinternals/2009-05/windows_vista_apc_internals.pdf
     
    APC基础概念

    APCs作为windows提供的一种机制,它能让一个线程从正常的执行流程转到执行其它代码。另外,apc的一个重要的特点就是当被执行的时候,是可以指定一个线程作为它的运行环境的。

    APCs很少有官方文档化的介绍:内核使用的API是没有公开的,而且它们的内部原理只有部分作了介绍。关于 apc,最有趣的是它们和windows的线程dispatch联系的特别紧密,所以,通过分析apc的机制,我们可以从侧面更好的理解windows的内核功能特性。

    一些讲解windows 内核机制的书本经常有提到一个这样的情况,apc是通过软中断来派发执行的。在这里就引出了另一个问题:操作系统是怎么保证这个中断会在一个特定的线程中执行呢?换言之也就是怎么保证一个APC在特定的线程中执行的呢?而且软中断是可以中断到任何的线程中的。对这个问题,我们下回分解。

    根据APC类型的不同,通过APC执行的代码会执行在一个合适的irql level,没错,这个irql level就叫apc level.

    那么,这些APC用来干什么的呢?

    •   I/O manager这个windows组件可以使用一个APC来完成一次i/o请求,并且可以在当初发起       这个i/o请求的线程中完成
    • 当要结束一个进程的时候,一个特殊的apc就能派上用场了,因为它能插入到一个正在执行中的进程
    • 当使用windows api像QueueUserAPC,ReadFileEx/WriteFileEx(异步IO方式)时,这些实现的内部就是通过APC来完成的

    APC进程环境

    通常来说,一个线程的执行环境就是当初那个创建它的那个原始进程(这里我们可以叫原始环境),但是,也有可能一个线程attach到另一个进程中去,也就是说,在这种情况下这个线程它就会执行在它attach的那个进程环境中(这里我们可以叫attach环境)
     
    在管理APC的时候,WINDOWS也支持了这种场景:执行apc代码的线程可以运行在一个原始环境中,也可以在attach环境中。
     
    Windows使用内核结构_KAPC_STATE来维护将要执行的APC的状态,结构如下:
    kd> dt nt!_KAPC_STATE
    +0x000 ApcListHead : [2] _LIST_ENTRY
    +0x010 Process : Ptr32 _KPROCESS
    +0x014 KernelApcInProgress : UChar
    +0x015 KernelApcPending : UChar
    +0x016 UserApcPending : Uchar
     
    windows内核里有一个很重要的内核结构_KTHREAD,在这个结构里面有2个这样的_KAPC_STATE结构成员,分别叫ApcState和SaveApcState,这2个也是传说中的APC环境,当指定APC要在当前线程运行时的环境中运行时,ApcState是用来保存这类APC环境的,不管这个线程是否有attach到其它进程,ApcState包含着当前进程环境信息,也就是可以派发的那些,SavedApcState 保存的APC信息表示不是当前进程可派发的,而且也需要等待其它时候才能使用,例如当一个线程attach到一个不是它原始的进程的时候,而你又指定一个APC需要在目标线程的原始环境中才能执行时,这个时候,这些APC就需要保存到SavedApcState中,同时需要等待线程从attac环境切回原始环境,即回到原始进程环境是时才有可能被派发。
     
    从上面的解释可以看出,当一个线程attach到其它进程时,attach之前的ApcState 会被copy到SavedApcState结构中去,同时会重新初始化。当线程Detach出来时,就会把apc信息从SavedApcState中copy回来到ApcState中,同时SavedApcState就清空了。从这里我们也可以看出,内核分发APC时,是从ApcState中查找可派发的APC的
     
    在线程结构体_KTHREAD中,也存在着一个指针数组,叫ApcStatePointer,这里面的元素保存的是ApcState和SavedApcState的地址,内核中时刻维护着这个数组,保证这个数组的第一个元素永远指向的apc环境是线程的原始环境,而第二个是指向attach环境。
     
    例如,当一个线程没有attach到其它进程的时候,这个线程正在执行的进程环境就是当前激活的那个,因此,这个线程的当前执行环境就是保存在ApcState中,同时ApcStatePointer[0]也是指向这个ApcState.
     
    最后,在_KTHREAD结构中还有一个成员叫ApcStateIndex保存着一个索引,这个索引会指向当前使用的环境,当没attach时会指向ApcState,反之指向SavedApcState。所以,如果一个线程attach到其它进程时,这个线程的原始环境就会保存回SaveApcState中,那ApcStatePointer[0]也会指向SavedApcState,因为第一个元素永远指向着它的原始环境,接着,ApcStatepointer[1]指向ApcState,因为它永远指向着被atached的进程的环境。所以ApcStateIndex就会被设置成1,这是因为apcStatePointer[1]必须指向的是当前使用的进程环境
     
    当我们要使用一个APC的时候,我们可以指定把这个APC放在ApcStatePointer指向的任何一个环境中执行,如果是原始环境还是attach环境,还是指定当前真正使用的环境(要么是原始环境要么是attach环境)
     
     
     
    APC类型
     
    在Windows操作系统中,APC一共有3种类型:
     
    • 特殊模式的内核APC(下方叫SK apc)
        这种APC执行在内核模式下,同时它的irql是apc level.这种APC是真正可以打断一个正在运行的线程,并转向执行其它内核模式函数的(这个函数就是APC中的KernelRoutine),而且这个打断的行为是异步的.
     
        当一个线程由于调用了下面几类函数KeWaitforSingleObject,KeWaitForMutipleObjects,KeWaitforMutexObject或者KeDelayExecution而进入了wait状态时,如果给这个线程投递一个sk apc的话,这个线程就会被unwait出来,
        并且会去执行这个sk apc的KernelRoutine,但在执行完这个Routine后,它又会重新进入wait状态.
     
              通常来说,sk apc总会被派发执行的只要它的目标线程正在执行同时irql降到低于apc level一般是passive level时。但有时候 apc也会被disable掉的,例如当线程_KTHREAD的SpecialApcDisable字段不为0时,这种情况下   
              ,所有的类型的APC都会被disable,包括sk apc.
     
    • 普通模式的内核APC
                   这种APC也是执行在内核模式下,但它的运行时irql level是passive level。和sk apc一样,这种APC也可以打断正在运行的线程,同时能把线程从wait状态unwait过来。但是这种APC的执行条件有一定的限制,具体细节后面分解。
     
    • 用户模式APC
                   这种APC只会执行用户模式的代码,并且执行的要求条件比内核APC更多限制,如这种APC只会在目标线程将要进入alertable类型的等待状态时才会被派发运行(不过特殊的情况下不需要,后面会讲解)。
                   那这个alertable类型的等待状态什么情况下会发生呢,一般线程调用下面几个函数,并且参数传递的是Alertable = true and WaitMode = User时,就会进入这种状态.KeWaitForSingleObject, KeWaitForMultipleObjects,
                   KeWaitForMutexObject, orKeDelayExecutionThread
     
             因此,正常情况下用户模式APC并不会打断一个线程的执行流程,它们更像一种可以放入队列的工作项:即在任何时候可以把这个工作项放到一个线程中,同时这个线程决定什么时候来执行这个工作项.
                   
                   
  • 相关阅读:
    A Simple Problem with Integers poj 3468 多树状数组解决区间修改问题。
    Fliptile 开关问题 poj 3279
    Face The Right Way 一道不错的尺取法和标记法题目。 poj 3276
    Aggressive cows 二分不仅仅是查找
    Cable master(二分题 注意精度)
    B. Pasha and String
    Intervals poj 1201 差分约束系统
    UITextField的快速基本使用代码块
    将UIImage转换成圆形图片image
    color转成image对象
  • 原文地址:https://www.cnblogs.com/kkindof/p/4831689.html
Copyright © 2011-2022 走看看