天天看点

Windows内核的基本概念

 Windows内核的基本概念

1  处理器模式

Windows使用 0 和3 两种特权级。0 表示CPU处于内核模式(kernel mode);3 表示用户模式(user mode)。

1.1  内存管理

Windows 将32位虚拟内存空间按照0~4 GB的线性地址空间看待。其中2~4GB是所有进程共享的,为系统地址空间。0~2GB 为进程地址空间。

在系统地址空间中,典型的三种内存页面管理算法:

  1. 非换页内存池。这部分内存区域在初始化时已经被映射到物理页面,所以Windows利用空闲链表的做法,按照不同的粒度(1、2、3、>=4 个页面的大小),将空闲页面链接起来,空闲页面本身即链表的结点,申请和释放页面的操作实际上是针对空闲链表来进行的。
  1. 换业内存池。在换页内存池区域,空闲的页面并没有被映射好物理页面,Windows使用位图来管理页面的分配。分配连续的多个页面,即从位图中找到连续的零位。
  2. 系统PTE区域。把PTE当作资源来管理。当内核需要一段虚拟地址来映射物理页面时,它可以使用系统PTE区域中的地址。

1.2  进程和线程管理

进程定义了一个执行环境,包括私有的地址空间、一个句柄表、以及一个安全环境。

线程则是一个控制流,有自己的调用栈,记录了它的执行历史。

Windows 中的进程和线程的关系:

Windows内核的基本概念

1.3  中断和异常

中断(interrupt) 是处理器与外部设备打交道的重要途经; 异常(exception) 是处理器的正常指令流在执行过程中产生的一下特殊事件,需要紧急处理才能继续原来的指令流。

Intel x86 利用IDT(Interrupt Descriptor Table,中断描述符表),将中断或异常与一个处理服务例程关联,一旦发生中断或异常,该服务例程即被执行。

Windows 允许设备驱动程序为特定的中断向量添加它的中断服务例程(ISR ,Interrupt Service Routine)。

Windows 规定了一套软件中断优先级,称为中断请求级别(IROL,Interrupt Request Level),Windows 使用0~31来表示IRQL,数值越大,优先级越高。

IRQL=0 表示普通线程,称为PASSIVE_LEVEL或被动级别;

IRQL=1 表示异步过程调用(APC ,Asynchronous Procedure Call),称为APC_LEVEL;

IRQL=2 表示延迟过程调用(DPC,Deferred Procedure Call),称为DISPATCH/DPC或者DISPATCH_LEVEL。

3~26是设备IRQL,27~31是一些特殊的硬件中断。

内核模式异常处理:异常分发器首先将异常交给内核调试器来处理,若不存在内核调试器或者内核调试器没有处理该异常,则尝试分发到一个基于帧的异常处理器(frame-based exception handler)。

用户模式异常处理:异常分发器首先判断进程的调试端口是否有效,若有效,则发送消息至调试端口,然后等待应答,否则将异常交给内核调试器。如果异常仍未得到处理,则将控制转到用户模式下,由用户模式的异常分发器(ntdll.dll 中的KeUserExceptionDispatcher 函数)寻找一个基于帧的异常处理器。如果在用户模式下该异常仍为得到处理,则控制再次回到内核模式下。这次,内核模式的异常处理器首先尝试调试端口,若异常仍未被处理,则再尝试当前进程的异常端口。连接进程异常端口的是Windows子系统,因此,Windows子系统在异常处理的最后时刻由机会处理它所属进程的异常。如果它也不能处理,则该进程被终止。

1.4  同步

同步是为了解决并发访问。windows操作系统提供多种多种同步机制,根据执行环境中的IRQL值大于APC_LEVEL或者等于PASSIVE_LEVEL,可以将同步分为“不依赖线程调度的同步机制”和“基于线程调度的同步机制”两类。

当IRQL大于APC_LEVEL时,Windows提供了一些方便的同步保护机制,供内核自身或设备驱动程序使用。IRQL大于APC_LEVEL时典型的同步机制如下:

  1. 提升IRQL。根据IRQL的定义,当处理器在某个IRQL上运行时,他只能被更高IRQL的中断打断,意味着,不用担心低IRQL的过程会抢占掉当前执行过程。这种做法在单处理器系统上可以做的更好,但是多处理器系统上,提升IRQL还不够,往往还需要其他同步机制(如自旋锁)。
  2. 互锁操作。利用Intel X86处理器提供的lock指令前缀,可以实现基于整数操作的保护,确保一个操作以原子方式进行。这种操作只能在小粒度数据上(可以达到64位内存单元)进行同步保护,而且是指令级保护。
  3. 无锁的单链表。windows利用64位互锁指令来实现无锁的单链表数据结构,支持多核,多处理器环境。
  4. 自旋锁(spin lock)。自旋锁本身是一种忙等。为了获得自旋锁,处理器持续监测锁状态。直至可用。此时即使有线程调度执行,APC排队,也没机会执行。所以自旋锁通常用于IRQL大于等于DISPATCH_LEVEL的代码。windows还提供一些自旋锁的扩展:执行体自旋锁(支持共享和独占的语义),排队自旋锁(queued spin lock)和栈内排队自旋锁(in-stack queued spin lock)。

IRQL值等于PASSIVE_LEVEL线程之间的同步。

windows定义了统一的机制支持各种线程同步原语:分发器对象(dispatcher object),其数据结构头部为DISPATCH_HEADER。

windows Server 2003 实现了以下分发器对象:

  1. 事件(event):分为事件通知对象和事件同步对象,区别在于,当事件对象变成有信号状态时,是解除所有正在等待该对象的线程,还是只唤醒第一个以WaitAny方式等待该对象的线程。
  2. 突变体(mutant):突变体是Windows内核中互斥体的实现,如果突变体对象为无信号状态,则一定被某个线程“占有”,否则可满足任何一个线程的等待要求。突变体记录了“所有者”线程信息,通常可用于实现“锁”。
  3. 信号量(semaphore):信号量有一个计数器,用于控制最多可以有多少个线程共享一个资源。当计数器达到预定最大值时,信号量将变成无信号状态。
  4. 队列对象(queue):这是Windows内核中用于支持线程池的机制,其数据结构为KQUEUE,通常可以用于控制一项任务的并发程度。它的典型用途是I/O完成端口。
  5. 进程对象:Windows的内核进程对象本身也是一个分发器对象,当进程对象被初始创建时,为无信号状态,进程结束为有信号状态。
  6. 线程对象:同理如上。
  7. 定时器对象:定时器对象既是一个像DPC一样的例程,也是个可等待的分发器对象,当设定的时间到期时,定时器变成有信号状态。
  8. 门对象(gate object):在windows中,门对象和门等待时线程调度器的特殊支持,它绕过了以上分发器对象同步过程中的很多步骤。唤醒一个门等待的线程几乎是以最快捷的方式进行,线程调度器会直接将线程插入到某个处理器的就绪队列中。

除此之外,还有更丰富的同步机制:包括,快速互斥体(fast mutex),守护互斥体(guarded mutex),执行体资源(executive resource)和推锁(push lock)。