windows核心编程7

 
第7章线程的调度、优先级和亲缘性
抢占式操作系统必须使用某种算法来确定哪些线程应该在何时调度和运行多长时间。
每个线程拥有一个上下文结构的,这个结构维护在线程的内核对象中。这个上下文结构反映了线程上次运行时该线程的C P U寄存器的状态。每隔2 0 m s左右,Wi n d o w s要查看当前存在的所有线程内核对象。在这些对象中,只有某些对象被视为可以调度的对象。Wi n d o w s选择可调度的线程内核对象中的一个,将它加载到C P U的寄存器中,它的值是上次保存在线程的环境中的值。这项操作称为上下文转换。Wi n d o w s实际上保存了一个记录,它说明每个线程获得了多少个运行机会。使用M i c r o s o f tS p y + +这个工具,就可以了解这个情况。
目前,线程正在执行代码,并对它的进程的地址空间中的数据进行操作。再过2 0 m s左右,Wi n d o w s就将C P U的寄存器重新保存到线程的上下文中。线程不再运行。系统再次查看其余的可调度线程内核对象,选定另一个线程的内核对象,将该线程的上下文加载到C P U的寄存器中,然后继续运行。当系统引导时,便开始加载线程的上下文,让线程运行,保存上下文和重复这些操作,直到系统关闭。
总之,这就是系统对线程进行调度的过程。这很简单,是不是? Wi n d o w s被称为抢占式多线程操作系统,因为一个线程可以随时停止运行,随后另一个线程可进行调度。如你所见,可以对它进行一定程度的控制,但是不能太多。记住,无法保证线程总是能够运行,也不能保证线程能够得到整个进程,无法保证其他线程不被允许运行等等。
 
注意:程序员常常问我,如何才能保证线程在某个事件的某个时间段内开始运行,比如,如何才能确保某个线程在数据从串行端口传送过来的1 m s内开始运行呢?我的回答是,办不到。实时操作系统才能作出这样的承诺,但是Wi n d o w s不是实时操作系统。实时操作系统必须清楚地知道它是在什么硬件上运行,这样它才能知道它的硬盘控制器和键盘等的等待时间。M i c r o s o f t对Wi n d o w s规定的目标是,使它能够在各种不同的硬件上运行,即能够在不同的C P U、不同的驱动器和不同的网络上运行。简而言之,Wi n d o w s没有设计成为一种实时操作系统。
 
尽管应强调这样一个概念,即系统只调度可以调度的线程,但是实际情况是,系统中的大多数线程是不可调度的线程。例如,有些线程对象的暂停计数大于1。这意味着该线程已经暂停运行,不应该给它安排任何C P U时间。通过调用使用C R E AT E _ S U S P E N D E D标志的C r e a t e P r o c e s s或C r e a t e T h r e a d函数,可以创建一个暂停的线程。(本章后面还要介绍S u s p e n dT h r e a dR e s u m e T h r e a d函数。)
 
除了暂停的线程外,其他许多线程也是不可调度的线程,因为它们正在等待某些事情的发生。例如,如果运行N o t e p a d,但是并不键入任何数据,那么N o t e p a d的线程就没有什么事情要做。系统不给无事可做的线程分配C P U时间。当移动N o t e p a d的窗口时,或者N o t e p a d的窗口需要刷新它的内容,或者将数据键入N o t e p a d,系统就会自动使N o t e p a d的线程成为可调度的线程。这并不意味着N o t e p a d的线程立即获得了C P U时间。它只是表示N o t e p a d的线程有事情可做,系统将设法在某个时间(不久的将来)对它进行调度。
 
7.1 暂停和恢复线程的运行
在线程内核对象的内部有一个值,用于指明线程的暂停计数。当调用C r e a t e P r o c e s s或C r e a t e T h r e a d函数时,就创建了线程的内核对象,并且它的暂停计数被初始化为1。这可以防止线程被调度到C P U中。当然,这是很有用的,因为线程的初始化需要时间,
你不希望在系统做好充分的准备之前就开始执行线程。当线程的暂停计数是0的时候,除非线程正在等待其他某种事情的发生,否则该线程就处于可调度状态。
 
在暂停状态中创建一个线程,就能够在线程有机会执行任何代码之前改变线程的运行环境(如优先级)。一旦改变了线程的环境,必须使线程成为可调度线程。要进行这项操作,可以调用R e s u m e T h r e a d。如果R e s u m e T h r e a d 函数运行成功,它将返回线程的前一个暂停计数 。单个线程可以暂停若干次。如果一个线程暂停了3次,它必须恢复3次,然后它才可以被分配给一个C P U。当创建线程时,除了使用C R E A T E _ S U S P E N D E D外,也可以调用S u s p e n dT h r e a d函数来暂停线程的运行。任何线程都可以调用该函数来暂停另一个线程的运行(只要拥有线程的句柄)。不用说,线程可以自行暂停运行,但是不能自行恢复运行。在实际环境中,调用S u s p e n d T h r e a d时必须小心,因为不知道暂停线程运行时它在进行什么操作。只有确切知道目标线程是什么(或者目标线程正在做什么),并且采取强有力的措施来避免因暂停线程的运行而带来的问题或死锁状态,S u s p e n d T h r e a d才是安全的。
 
7.2 暂停和恢复进程的运行
对于Wi n d o w s来说,不存在暂停或恢复进程的概念,因为进程从来不会被安排获得C P U时间。
 
7.3 睡眠方式
该函数可使线程暂停自己的运行,直到d w M i l l i s e c o n d s过去为止。关于S l e e p函数,有下面几个重要问题值得注意:
• 调用S l e e p,可使线程自愿放弃它剩余的时间片。
• 系统将在大约的指定毫秒数内使线程不可调度。不错,如果告诉系统,想睡眠1 0 0 m s,那么可以睡眠大约这么长时间,但是也可能睡眠数秒钟或者数分钟。记住, Wi n d o w s不是个实时操作系统。虽然线程可能在规定的时间被唤醒,但是它能否做到,取决于系统中还有什么操作正在进行。
• 可以调用S l e e p,并且为d w M i l l i s e c o n d s参数传递I N F I N I T E。这将告诉系统永远不要调度该线程。这不是一件值得去做的事情。最好是让线程退出,并还原它的堆栈和内核对象。
• 可以将0传递给S l e e p。这将告诉系统,调用线程将释放剩余的时间片,并迫使系统调度另一个线程。但是,系统可以对刚刚调用Sleep的线程重新调度。如果不存在多个拥有相同优先级的可调度线程,就会出现这种情况。
 
7.4 转换到另一个线程
系统提供了一个称为S w i t c h T o T h r e a d的函数,使得另一个可调度线程(如果存在能够运行)。当调用这个函数的时候,系统要查看是否存在一个迫切需要C P U时间的线程。如果没有线程迫切需要C P U时间,S w i t c h To T h r e a d就会立即返回。如果存在一个迫切需要C P U时间的线程,S w i t c h To T h r e a d就对该线程进行调度(该线程的优先级可能低于调用S w i t c h To T h r e a d的线程)。这个迫切需要C P U时间的线程可以运行一个时间段,然后系统调度程序照常运行。
 
7.5 线程的运行时间
Wi n d o w s提供了一个称为G e t T h r e a d Ti m e s的函数。
 
7.6 运用结构环境
现在应该懂得环境结构在线程调度中所起的重要作用了。环境结构使得系统能够记住线程的状态,这样,当下次线程拥有可以运行的C P U时,它就能够找到它上次中断运行的地方。
 
“C O N T E X T结构包含了特定处理器的寄存器数据。系统使用C O N T E X T结构执行各种内部操作。目前,已经存在为I n t e l、M I P S、A l p h a和P o w e r P C处理器定义的C O N T E X T结构。若要了解这些结构的定义,参见头文件Wi n N T. h”。
 
C O N T E X T结构中究竟存在哪些东西呢?它包含了主机C P U上的每个寄存器的数据结构。在x 8 6计算机上,数据成员是E a x、E b x、E c x、E d x等等。如果是A l p h a处理器,那么数据成员包括I n t V 0、I n t T 0、I n t T 1、I n t S 0、I n t R a和I n t Z e r o等等。
 
C O N T E X T结构可以分成若干个部分。C O N T E X T _ C O N T R O L包含C P U的控制寄存器,比如指令指针、堆栈指针、标志和函数返回地址(与x 8 6处理器不同,Alpya CPU在调用函数时,将该函数的返回地址放入一个寄存器中)。C O N T E X T _ I N T E G E R用于标识C P U的整数寄存器。C O N T E X T _ F L O AT I N G _ P O I N T用于标识C P U的浮点寄存器。C O N T E X T _ S E G M E N T S用于标识C P U的段寄存器(仅为x 8 6处理器)。CONTEXT_DEBUG_ REGISTER用于标识C P U的调试寄存器(仅为x 8 6处理器)。CONTEXT_EXTENDED_ REGISTERS用于标识C P U的扩展寄存器(仅为x 8 6处理器)。
 
Wi n d o w s实际上允许查看线程内核对象的内部情况,以便抓取它当前的一组C P U寄存器。若要进行这项操作,只需要调用G e t T h r e a d C o n t e x t函数。
 
G e t T h r e a d C o n t e x t和S e t T h r e a d C o n t e x t函数使你能够对线程进行许多方面的控制,但是在使用它们时应该小心。实际上,几乎没有应用程序调用这些函数。增加这些函数是为了增强调试程序和其他工具的功能。任何应用程序都可以调用它们。
 
7.7 线程的优先级
每个线程都会被赋予一个从0(最低)到31(最高)的优先级号码。当系统确定将哪个线程分配给C P U时,它首先观察优先级为31的线程,并以循环方式对它们进行调度。如果优先级为31的线程可以调度,那么就将该线程赋予一个C P U。在该线程的时间片结束时,系统要查看是否还有另一个优先级为31的线程可以运行,如果有,它将允许该线程被赋予一个C P U。
 
只要优先级为31的线程是可调度的,系统就绝对不会将优先级为0到30的线程分配给C P U。这种情况称为渴求调度( s t a r v a t i o n)。当高优先级线程使用大量的C P U时间,从而使得低优先级线程无法运行时,便会出现渴求情况。在多处理器计算机上出现渴求情况的可能性要少得多,因为在这样的计算机上,优先级为3 1和优先级为3 0的线程能够同时运行。系统总是设法使C P U保持繁忙状态,只有当没有线程可以调度的时候, C P U才处于空闲状态。
 
高优先级线程将抢在低优先级线程之前运行,不管低优先级线程正在运行什么。例如,如果一个优先级为5的线程正在运行,系统发现一个高优先级的线程准备要运行,那么系统就会立即暂停低优先级线程的运行(即使它处于它的时间片中),并且将C P U分配给高优先级线程,使它获得一个完整的时间片。还有,当系统引导时,它会创建一个特殊的线程,称为0页线程。该线程被赋予优先级0,它是整个系统中唯一的一个在优先级0上运行的线程。当系统中没有任何线程需要执行操作时,0页线程负责将系统中的所有空闲R A M页面置0。
 
7.8 对优先级的抽象说明
调度算法对用户运行的应用程序类型有着相当大的影响。从一开始, M i c r o s o f t的开发人员就认识到,随着系统的用途的变化,他们必须不断修改调度算法。但是,软件开发人员需要在今天编写软件,而M i c r o s o f t则要保证软件能够在将来的系统版本上运行。那么M i c r o s o f t如何改变系统工作的方式并仍然保证软件能够运行呢?下面是解决这个问题的一些办法:
• Microsoft没有将调度程序的行为特性完全固定下来。
• Microsoft没有让应用程序充分利用调度程序的特性。
• Microsoft声称调度程序的算法是变化的,在编写代码时应有所准备。
Windows API展示了系统的调度程序上的一个抽象层,这样就永远不会直接与调度程序进行通信。
 
注意:有些人常常搞不清进程优先级类的概念。他们认为这可能意味着进程是可以调度的。但是进程是根本不能调度的,只有线程才能被调度。进程优先级类是个抽象概念,Microsoft提出这个概念的目的,是为了帮助你将它与调度程序的内部运行情况区分开来。它没有其他目的。
 
注意:一般来说,大多数时候高优先级的线程不应该处于可调度状态。当线程要进行某种操作时,它能迅速获得C P U时间。这时线程应该尽可能少地执行C P U指令,并返回睡眠状态,等待再次变成可调度状态。相反,低优先级的线程可以保持可调度状态,执行大量的C P U指令来进行它的操作。如果按照这些原则来办,整个操作系统就能正确地对用户作出响应。
 
7.9 程序的优先级
注意:Wi n d o w s没有提供返回线程的优先级的函数。
 
7.10 亲缘性
按照默认设置,当系统将线程分配给处理器时, Windows 2000使用软亲缘性来进行操作。这意味着如果所有其他因素相同的话,它将设法在它上次运行的那个处理器上运行线程。让线程留在单个处理器上,有助于重复使用仍然在处理器的内存高速缓存中的数据。
 
计算机在引导时,系统要确定机器中有多少个C P U可供使用。通过调用G e t S y s t e m I n f o函数,应用程序就能查询机器中的C P U数量。按照默认设置,任何线程都可以调度到这些C P U中的任何一个上去运行。为了限制在可用C P U的子集上运行的单个进程中的线程数量,可以调用S e t P r o c e s s A ff i n i t y M a s k
注意:子进程可以继承进程的亲缘性。
还有一个函数也能够返回进程的亲缘性位屏蔽,它就是G e t P r o c e s s A ff i n i t y M a s k
通过调用S e t T h r e a d A ff i n i t y M a s k,就能为各个线程设置亲缘性屏蔽。
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值