注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

写着玩

Bob

 
 
 

日志

 
 
 
 

陷阱调度--5  

2009-07-26 23:57:22|  分类: Win32 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

http://hi.baidu.com/l_wait/blog/item/04b89d8b91eb86d7fc1f105f.html

软件中断

虽然硬件产生了大部分中断,但是Windows内核也为不同的任务准备了软件中断,包括:

·开始线程调度

·不限时中断处理

·处理计时器溢出

·在指定线程的上下文中异步地执行一个过程

·支持异步I/O操作

延迟过程调用(Dispatch/Deferred Procedure Call)中断

(Win32 核心 DPC 设计思想和实现思路浅析)

   一个线程如果不能继续执行,可能是因为它已经结束了或者它自动进入了等待状态,此时内核就会直接调用调度程序做一个上下文切换。但是,内核可能检测到在做这次重新调度的时候,中断嵌套已经很深了。在这种情况下,内核就会考虑将调度延迟到它干完手头的工作。而一个DPC软件中断可以方便地实现这种延迟。

       当处理器要同步访问共享的内核结构时,内核就会提升处理器的IRQL到DPC/dispatch或更高级别。这样就禁止了其他软件中断以及线程调度。当内核知道要进行调度,就会要求一个DPC/dispatch级别的中断;但是如果IRQL正处于或高于这个级别,处理器就会阻止这个中断。当内核完成了当下的工作,它要确保将IRQL降到DPC/dispatch级别以下,并且检查是否有调度中断发生。如果有,就把IRQL降到DPC/dispatch级别,然后处理调度中断。使用软件中断来启动线程调度是一种将调度推迟到情况允许的时候再进行的方法。Windows也会用软件中断去延迟其他处理过程。

      除了线程调度,内核也会在这个IRQL进行延迟过程调用(deferred procedure calls,DPCs)。DPC是一个用来完成系统任务的函数——这个任务没有当前任务那么紧迫。之所以叫做延迟,是因为它们不会马上就被执行。

       DPCs让操作系统能够产生出一个中断并且在内核模式执行一个系统函数。内核用DPCs处理计时器溢出(并且释放那些等待该计时器的线程)以及在某线程的时间片用完之后,重新调度处理器。设备驱动使用DPCs完成I/O请求。为了能向硬件中断提供及时的服务,Windows——加上设备驱动的配合——努力把IRQL降到device级别以下。实现这一目标的方法是让设备驱动ISRs以最小的成本识别设备,保存易失的中断状态,并且将数据传输或者其他不那么紧迫的中断处理工作放到DPC/dispatch IRQL的DPC中去做。

       DPC被表示为一个DPC物件,DPC物件是一个内核控件,它对用户模式的程序不可见,对设备驱动和其他系统代码可见。DPC物件中最重要的信息是内核在处理DPC中断时会用到的系统函数的地址。待执行的DPC例程被存储在一个由内核管理的队列中,每个处理器一队,叫做DPC队列。为了调用一个DPC,系统代码会让内核初始化一个DPC文件,并将它列入DPC队列。

    默认情况下,内核会把DPC物件放到调用DPC的处理器(它很可能是刚刚执行过ISR的)的DPC对列的队尾。当然设备驱动可以改写这一行为,比如通过指定DPC的优先级(分为低、中、高,默认值为中)或者将DPC指定到某个处理器。一个指向特定CPU的DPC被认为是有目标的DPC。如果DPC的优先级为中或低,内核就会让这个DPC物件排到队尾;如果DPC有一个高优先级,内核就会允许这个DPC物件插队到最前面。

    在处理器的IRQL快要从DPC/dispatch或更高级别降至较低级别(APC或无源级别)时,内核开始处理DPCs。Windows确保IRQL待在DPC/dispatch级别好将DPC物件一一踢出处理器队列,在让队列为空( 即内核“排空”列)之前依次呼叫DPC函数。只有在队列空了之后内核才会把IRQL降到DPC/dispatch以下的级别,让一般的线程继续执行。

    DPC优先级还在其他方面影响系统的行为。内核一般是以一个DPC/dispatch级别的中断开始排空DPC队列的。只有在DPC正好处于执行完ISR的处理器上,并且这个DPC的优先级为高或者中时,内核才会产生一个前述中断。如果DPC为低优先级,那么只有当待处理的DPC的数量超过队列长度的最大值,或者当DPC的请求速率低于最小值时,内核才会申请前述中断。如果DPC的目标CPU不是之前执行ISR的那个,而DPC是高优先级,内核就会立即知会目标CPU(向它发出一个IPI)去排空它的DPC队列。如果DPC的优先级为中或者低,那么在目标处理器上的DPCs数量必须超出队列长度的最大值,内核才会触发一个DPC/dispatch中断。系统空闲线程也会为运行它的处理器排空DPC队列。虽然DPC的目标和优先级是可变的,但是设备驱动极少会去改变DPC物件的默认行为。

       由于用户模式的线程在低IRQL运行,这方便DPC中断一个普通用户线程的执行。DPC例程并不关心哪个线程正在执行,也就是说DPC例程在执行时并不知道当前映射的是哪个进程的地址空间。DPC例程可以调用内核函数,但不能调用系统服务,不会产生缺页错误,不会产生或等待调度物件。但是,DPC可以访问未分页的系统内存地址,因为系统地址空间的映射位置与当前进程无关。

    DPCs主要用于设备驱动,但是内核也会用到它们。内核常常使用DPC来处理时间片溢出。系统时钟的每一拍都会产生一个clock级别的中断。时钟中断处理程序(运行在clock级别)会更新系统时间,并且递减跟踪线程执行的计数器。当该计数器降到0,线程的时间片就用完了,于是内核需要重新调度处理器,这是一个要在DPC/dispatch级别执行的低优先级任务。时钟中断处理程序使用一个DPC来开始线程调度,完成它的工作之后再降低处理器的IRQL 。

//EXPERIMENT: Monitoring Interrupt and DPC Activity

异步过程调用(Asynchronous Procedure Call,APC)中断

   APCs提供了一种使得用户程序和系统代码可以在特定用户线程的上下文(也就是特定的进程地址空间)中执行的方法。由于APCs等着在特定线程的上下文中执行,并且它的运行级别低于DPC/dispatch,所以它不需要像DPC一样受限。一个APC例程可以获得资源(物件),等待物件句柄,可能遇到缺页错误,可以调用系统服务。

    APCs被定义成叫做APC物件的内核控件。等待执行的APCs待在一个内核管理的APC队列中。不像DPC队列是隶属于系统的,APC队列属于线程——每个线程有一个自己的APC队列。当需要用到一个APC,内核会把它插进将会执行这个APC的线程的队列中。内核会依次请求APC级别的软件中断,一旦线程开始,它就会执行APC。

       有两种APCs:内核模式APCs和用户模式APCs。内核模式APCs要在目标线程的上下文中执行是不需要得到许可的,而用户模式APCs则无此特权。内核模式APCs中断一个线程以执行某个过程是不需要该线程的参与和批准的。内核模式的APCs也分为两种:普通APCs和特殊APCs。线程可以禁止这两种APCs,只需要提升IRQL到APC级别,或者调用KeEnterGuardedRegion,这是Windows Server 2003引入的例程。KeEnterGuardedRegionThread是通过设置线程的KTHREA结构中的字段SpecialApcDisable来禁止APC的。线程可以调用KeEnterCriticalRegion来禁止普通APCs,该例程重置了线程的KTHREA结构中的字段KernelApcDisable。

    程序必须在特定线程的地址空间(上下文)中使用内核模式APCs完成操作系统任务。比如说可以让特定的内核模式APCs指挥线程叫停一个系统服务,或者记录下在某线程地址空间中异步I/O操作的结果。环境子系统用特定的内核模式APCs来中止或结束线程本身,获取或者设置线程用户模式的上下文。POSIX子系统用内核模式APCs模拟向POSIX处理器传递POSIX信号的动作。

   设备驱动也会用到APCs。例如,如果某线程启动了一个I/O操作,该线程即进入等待状态,会有另一个进程的线程被调来执行。当设备完成了数据传输,I/O系统必须回到启动这个I/O操作的线程的上下文,复制I/O操作的结果到线程隶属的进程的地址空间缓存区中。I/O系统使用了一个内核模式APC完成了这个动作。

       一些Windows APIs,如ReadFileEx、WriteFileEx,和QueueUserAPC,会用到用户模式APCs。例如,函数ReadFileEx和WriteFileEx允许调用者在结束I/O操作之后执行指定的完成例程(completion routine)。I/O完成例程把一个APC列入到实施I/O的线程队列中。但是,不必在APC 排队时回调完成例程,因为只有在线程处于待命等待状态时,用户模式APCs才被传递给线程。一个线程会进入等待状态,可能是因为它在等待某物件的句柄,并且指定其为待命等待(使用Windows函数WaitForMultipleObjectsEx),或者直接检查它是否有一个待执行的APC(使用SleepEx)。在以上情况下,如果有一个用户模式APC等待执行,内核就会中断线程,将控制交给APC例程,APC例程结束之后再恢复线程的执行。与内核模式APCs不同,用户模式APCs不是在APC级别,而是在无源级别执行。

       APC传递会重排等待队列——线程为了获得某个资源而排的队。如果一个APC送到的时候线程正处于等待状态,执行完APC,等待又重新开始。如果还需要等待,线程就恢复到等待状态,不过现在它只能排到队尾了。就比如说,APCs中止了一个线程的执行,如果此时线程正等待什么物件,等待就会被中止,直到线程重新开始执行,而彼时线程将不得不排在访问物件队列的队尾。

  评论这张
 
阅读(475)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017