1、陷阱分发:中断和异常是导致处理器转向正常控制流之外代码的两种操作系统条件。陷阱的定义如下:当异常或者中断发生时,处理器捕捉到一个执行线程,并且将控制权转移到操作系统中某一个固定地址处。在Windows系统中,处理器会将控制权转给陷阱处理器,所谓的陷阱处理器,是指与某个特定的中断或者异常相关联的函数。
中断是一个异步事件(可以在任何时候发生),并且与处理器当前正在执行的任务毫无关系。中断主要是由IO设备、处理器时钟或者定时器产生的,并且可以被启用(打开)或者禁用(关闭)。相反地,异常是一个同步条件,它往往是一个特殊指令执行的结果,在同样的条件下用同样的数据第二次运行程序可以重现原来的异常。异常的例子包括:内存访问违例、特定的调试器指令、以及除零错误等。内核把系统调用服务也看做异常(不过从技术上讲,它们是系统陷阱)。
当硬件异常或者中断产生时,处理器将在被中断的线程的内核栈中记录下足够多的机器状态信息,因而可以回到控制流中的该点处继续执行。如果该线程在用户模式下执行。那么Windows就切换到该线程的内核模式栈。然后,Windows在被中断线程的内核栈上创建一个陷阱栈帧,并把线程的执行状态保存到陷阱栈帧里。内核在处理软中断时,或者将软中断当做硬中断处理的一部分,或者当前线程调用与软中断相关的内核函数时以同步的当时进行处理。
硬件产生的中断往往是由IO设备激发的,当这些设备需要服务时,它们以中断的方式通知处理器。中断驱动的设备使操作系统可以交替地进行中心处理和IO操作,从而最大限度的发挥处处理器的能力。线程启动一个与设备之间传输数据的IO,然后在该设备完成此IO的过程中,线程可以执行其他有用的工作。当设备完成IO操作时,设备可以中断处理器,请求服务。
内核安装了中断陷阱处理器来响应设备的中断。中断陷阱处理器或者将控制权传递给一个负责处理该中断尔等外部例程ISR,或者传递给一个响应该中断的内部内核例程。设备驱动程序提供了ISR来处理设备终端,而内核为其他类型的中断提供了中断处理例程。
2、硬件中断处理:在Windows所支持的硬件平台上,外部IO中断进入到中断控制器的一根线上。该控制器接着在某一根线上中断处理器。处理器一旦被中断,就会询问中断控制器已获得中断请求IRQ。中断控制器会将该IRQ转译成一个中断号,利用中断号作为索引在中断分发表IDT的结构中找到一个IDT项,并将控制权传递给恰当的中断分发例程。在系统引导的时候,Windows就会填充中断分发表IOT,其中包含了指向负责处理每个中断和异常的内核例程的指针。Windows将硬件IRQ映射到IDT中的中断号上,同时利用IDT为异常配置陷阱处理器。每个处理器都有单独的IDT,所以如果合适,不同的处理器可以运行不同的ISR。
3、中断请求级别IRQL:虽然中断控制器已经实现了中断优先级,但是Windows仍然强制使用它自己的中断优先级方案,称之为中断请求级别IRQL。x86系统上,内核在内部使用0~31的数值来表示IRQL,而在x64系统上,内核采用0~15的数值来表示IRQL。数值越大,代表中断的优先级越高。内核为软件中断定义了一组标准的IRQL,而HAL则将硬件中断号映射为IRQL。
中断是按照优先级处理的,高优先级的中断会抢占低优先级中断的执行权。当一个高优先级的中断发生时,处理器把要中断的线程的状态保存起来并调用与该中断关联的陷阱分发器。该陷阱分发器提升IRQL,并调用该中断的服务例程ISR。在服务例程执行完以后,中断分发器再降低处理器的IRQL,使其回到中断发生前的级别,然后装入保存的机器状态。被中断的线程从原来停止的地方恢复运行。当内核降低IRQL时,被屏蔽的低优先级中断有可能得以出现。如果是这样,内核会重复上述过程来处理新的中断。IRQL是中断源的一个属性,而且每个处理器都有一个IRQL设置,它会随着操作系统代码的执行而变化。
4、中断屏蔽:每个处理器的IRQL设置决定了该处理器可以接收哪些中断。IRQL也被用来实现对某些内核模式的数据结构的同步访问。内核模式的线程运行时,它可以通过KeRaiseIrql或者KeLowerIrql直接地提升或者降低处理器的IRQL,或者通过通过调用那些获取内核同步对象的函数间接地提升或者降低处理器的IRQL。如下图所示,如果中断源的IRQL高于处理器当前的级别,则从该源处发生的中断源会打断处理器;如果中断源的IRQL等于或者低于处理器当前的级别,则这样的中断会被屏蔽。直到有一个正在执行的线程降低IRQL级别为止。
内核模式的线程根据想要做什么事情,来提升或者降低它运行所在处理器的IRQL。例如,当一个中断发生的时候,陷阱处理器(或者也可能是处理器本身)将该处理器的IRQL提升至中断源所分配的IRQL。这一提升会屏蔽住那些IRQL等于或者低于当前处理器的IRQL的所有中断(仅限于当前处理器),这可以确保当前中断不会被同一级别或者更低级别的中断劫掉。被屏蔽的中断可以由别的处理器来处理,或者被保留直到IRQL降下来。因此,系统的所有组件,包括内核和设备驱动程序,都试图让IRQL保持在被动级别(有时候也称为低端级别)上。
5、中断对象:内核提供了一种可移植的机制,使得设备驱动程序可以为它们的设备注册ISR。这种机制就是被称为中断对象的内核控制对象。中断对象包含了所有“供内核将一个设备的ISR与一个特定级别的中断关联起来而需要”的信息,包括该ISR的地址、设备中断时所在的IRQL,以及内核中与该ISR关联的IDT项。中断对象被初始化时,少量的汇编指令将被从中断处理模板KiInterruptTemplate中复制过来,保存在该对象中。当中断发生时,这些汇编指令将被执行。此类汇编指令被称为分发代码。与中断对象相关联的中断控制流如下图所示:
将一个ISR与某个特定级别的中断关联起来称为连接中断对象,而将一个ISR与一个IDT项断开关联,则称为断开中断对象。
6、异常分发:中断可以在任何时候发生,与此不同的是,异常是直接由当前正在运行的程序在执行过程中产生的条件。Windows使用了一种称为结构化异常处理的设施,使得应用程序可以在异常发生的时候获得控制。然后应用程序可以修正此条件并返回到异常发生处,将栈展开(从而使引发该异常的子例程的执行过程终止),或者系统报告该异常不可识别,系统应当继续搜索下一个有可能处理此异常的异常处理器。尽管可以通过语言的扩展访问异常处理机制,但实际异常处理是一种系统机制,并不是特定于某一语言的。除了部分简单到可以直接由陷阱处理器处理的异常外,其他的异常都是由异常分发器进行处理的。
当一个异常发生时,不管它是由软件显示发出的还是由硬件隐士发出的,在内核中都会发生一系列事件。CPU硬件将控制权传递给内核陷阱处理器,内核陷阱处理器创建一个陷阱帧(如同中断发生时那样)。正是由于该陷阱帧,在异常被解决以后,系统可以从它挺值得地方恢复运行。
所有的Windows线程都有一个异常处理器,负责处理所有未被处理的异常。这个异常处理器是在Windows内部的线程启动函数中声明的,线程启动函数是在用户创建进程或者任何额外线程的时候运行的。如果一个线程的异常没有被处理,则Windows的未处理异常过滤器将被调用。
7、对象管理器:执行体内部负责创建、删除黄、保护和跟踪对象的组件。对象管理器将那些本有可能散落在操作系统各处的资源控制操作集中在一起。在Windows中有三种类型的对象:执行体对象、内核对象和GDI/User对象。
8.1、执行体对象:指由执行体的各个组件(如进程管理、内存管理、IO子系统等等)所实现的对象;每个Windows环境子系统总是把操作系统的不同面貌呈现给它的应用程序。执行体对象和对象服务是环境子系统用于构建自有版本的对象和其他资源的基础。执行体对象往往由环境子系统代表用户拒应用程序而创建或者由操作系统的各种组件作为其常规操作的一部分而创建。Windows子系统使用执行体对象来导出自己的对象集合。执行体总共实现了42种对象类型,这些对象中有许多仅被用于其定义所在的执行体组件,无法通过Windows API直接访问。一些常见的执行体对象如下:
8.2、内核对象:指由Windows内核实现的一组更为基本的对象,这些对象对用户模式代码不可见,它们只是在执行体内部被创建和使用,内核对象提供了最为基本的能力,比如同步等,执行体对象的功能是建立在内核对象之上。因此,许多执行体对象封装了一个或者多个内核对象,如下图所示:
9、对象结构:如下图所示,每个对象都有一个对象头和对象体。对象管理器控制了对象头,而执行体组件则控制了由它们所创建的对象类型的对象体。每个对象头也有一个索引,指向一个被称为类型对象的特殊对象,该对象包含的信息对于它的每个实例是公共的。另外,还有可以有多达五个可选的子头:名称信息头、配额信息头、进程信息头、句柄信息头和创建者信息头。
9.1、对象头:对象管理器使用对象头中保存的数据来管理这些对象,而无需涉及其类型。对象头和对象子头的域如下图所示:
对象头中包含的信息适用于任何类型的对象,除此之外,子头中包含的可选信息仅涉及对象的某些特定的方面。
由于对象头和子头结构已经被标准化,因此对象管理器可以提供少量通用的服务对一个对象头中保存的属性进行操作,而且这些服务可以作用在任何类型的对象上(只不过有些通用服务对于某些特定的对象没有意义)。这些通用的服务如下表所示:
10、对象句柄:当一个进程根据名称来创建或者打开一个对象时,它接收到一个句柄,代表了此对象的访问。通过句柄来访问一个对象,要比直接使用名称来访问对象快得多,因为对象管理器可以跳过名称名称查找过程,从而直接找到目标对象。进程也可以在创建时刻,通过继承句柄的方式来获得对象的句柄(要求创建者在CreateProcess调用中制定了聚成句柄的标志并且句柄已经被标记为可继承的)。所有的用户模式进程在使用一个对象之前必须先拥有一个指向该对象的句柄。句柄被用作指向系统资源的间接指针,这一层间接性使得应用程序不用直接与系统数据结构打交道。对象句柄还有如下好处:
1)除了所指向的内容不同以外,文件句柄、事件句柄和进程句柄没有区别,这种相似性使得可以用一个统一的接口来引用对象而无须关心他们的类型(类似于面向接口编程);2)对象管理器有独占的权限来创建句柄以及找到一个句柄所指的对象,这意味着对象管理器可以仔细地审查每一个可能影响对象得用户模式动作,看一看调用者的安全轮廓是否允许在该对象上执行所请求的操作。
对象句柄是一个索引,指向进程特定的句柄表中的表项,执行体进程块中有一个域(字段)指向进程的句柄表。一个进程的句柄表包含了所有已经被该进程打开了句柄的那些对象的指针。句柄表的实现方式是一个三层结构,类似于从虚拟地址到物理地址的转译方式。只有底层的句柄表是在创建进程的时候被分配好的,其他的层都是根据需要而被创建。句柄表的布局结构如下图所示:
11、对象保持力:对象分为暂时的和永久的,大多数对象是暂时的,即它们只有在使用过程中才保留,当不再需要的时候就会被释放掉,永久对象会一直保留直至显示地释放掉。对象保持力被定义为:只有当暂时对象还在被使用时才会保留它们,等到用完以后就会将它们删除。因为所有的用户模式进程在访问一个对象之前首先需要打开一个指向该对象的句柄,所有对象管理器可以很容易地跟踪有多少个进程正在使用一个对象甚至哪些进程正在使用它。对象保持力是通过已打开句柄计数器和专门的引用计数实现的(类似于C++中智能指针的引用计数)。前者限用户进程使用,后者供操作系统使用和用户进程共同使用。
因此,即使一个对象的已打开句柄计数器达到了0,该对象引用计数仍然可能是一个正数,因为操作系统还有可能使用该对象。通常操作系统使用指针而不是句柄来访问对象(因此,对象管理器必须要记录下它已经给操作系统进程分配了多少个指针对象,每当对象管理器提供一个指向该对象的指针时,对象管理器就会递增一个专门用于该对象的引用计数,当系统递增句柄计数时,对象管理器也会递增该引用计数,同样地,当句柄计数递减的时候,对象管理器也会递减引用计数,因为对于这种必须跟踪的对象来说,句柄也是该对象的引用)。最终,当引用技术递减为0的时候,对象管理器就会将该对象从内存中删除。
对象泄露对于系统是很危险的,它泄露了内核内存池,最终会导致全系统范围内的内存缺失。内存泄漏可能会以各种微妙的形式打破应用程序。
13、自旋锁:在一般情况下,等待一个自旋锁意味着要使一个处理器停下来,因此自旋锁只能被用于以下一些严格受限的场合:1)对于受保护的资源必须快速访问,并且与其他代码没有复杂的交互关系;2)临界区代码的内存页不能交换出去,这些代码不能引用那些可能被换页的数据,不能调用外部过程(包括系统服务),也不能产生中断和异常;
12、分发器对象:内核以内核对象的形式,向执行体提供了额外的同步机制,这些内核对象合起来统称为分发器队形。那些对于Windows API 可见的同步对象正是从这些内核分发器对象中获得它们的同步能力。每个Windows API可见的且支持同步的对象都封装了至少一个内核分发器对象,通过WaitForSingleObject和WaitForMultipleObjects函数,执行体的同步原语对于Windows程序员是可见的。一个线程可以与一个分发器对象进行同步,做法是等待该对象的句柄,这样的做法使得内核将该线程处理等待状态。在任何给定的时刻,一个同步对象总是处于两种状态之一:有信号状态后这就无信号状态。一个线程在它的等地条件被满足以前不能恢复执行。
13、条件变量:可以同步一组正在等待某个结果进行条件测试的线程。
很多东西由于经验不足,暂时看不懂,后续有空再更新。