Windows Internals 笔记——线程优先级

1.每个线程都被赋予0(最低)~31(最高)的优先级数。当系统确定给哪个线程分配CPU时,它会首先查看优先级为31的线程,并以循环的方式进行调度。如果有优先级为31的线程可供调度,那么系统就会将CPU分配给该线程。在该线程的时间片结束时,系统查看是否还存在另一个优先级为31的线程可以运行,如果存在,它将获得CPU。

2.只要有优先级为31的线程可供调度,系统就不会给优先级0~30的线程分配CPU。这种情况称为饥饿。在多处理器机器上饥饿发生的可能性要小得多,因为这种机器上优先级为31和30的线程可以同时运行。

3.较高优先级的线程总是会抢占较低优先级的线程,无论较低优先级的线程是否正在执行。系统确定有较高优先级的线程已经准备好可以运行时,它会立即暂停较低优先级的线程(即使后者的时间片还没有用完),并将CPU分配给较高优先级的线程,该线程获得一个完整的时间片。

4.系统启动时,将创建一个名为页面清零线程的特殊线程。这个线程的优先级定为0,而且是整个系统中唯一一个优先级为0的线程。页面清零线程负责在没有其他进程需要执行的时候,将系统内存中的所有闲置页面清零。

5.Windows API在系统的调度程序之上提供了一个抽象层,因此我们不会直接调度调度程序,相反,调用的是Windows函数,它们会根据底层操作系统的版本来解释参数。

6.Windows支持6个优先级类:

  • idle优先级类非常适合只在系统什么都不做的时候运行的应用程序。
  • 只有在绝对必要的时候才使用high优先级。应该尽可能避免使用real-time优先级类,因为大多数操作系统线程在执行时所用的优先级类都比它低。
  • 99%的进程都是normal优先级类。
  • 进程不能运行在real-time优先级类,除非用户有Increase Scheduling Priority特权。默认情况下,隶属于管理员或者高级用户组的用户都具有这一权限。

7.Windows支持7个相对线程优先级,这些优先级是相对于进程优先级的。同样,大多数线程使用normal优先级。

8.概括起来,进程都属于某个优先级类,另外还可以指定进程中线程的相对线程优先级。 在不同版本的操作系统上映射(对应到0~31)是变化的。如下表是Windows Vista上的具体情况。

9.优先级0是保留给页面清零线程。除此之外,应用程序也无法获得一下优先级:17,18,19,20,21,27,28,29,30。但是如果编写的是运行在内核模式的设备驱动程序,那么我们可以获得这些优先级。用户模式的应用程序是不能获得这些优先级的。

10.real-time优先级类的线程,其优先级值不能低于16。同理,非real-time优先级线程的优先级的优先级值不能高于15。

11.一般而言,有较高优先级的线程大多数时候都应是不可调度的,当这种线程要执行什么任务时,很快就能得到CPU时间。这时,线程应该尽可能少地执行CPU指令,并重新进入睡眠,等待再次被调度。相反,优先级低的可以保持为可调度状态,执行大量CPU指令以完成其任务。

12.调用CreateProcess时,可以在fdwCreate参数中传入需要的优先级。

13.一旦进程运行,可以调用SetPriorityClass来改变自己的优先级。GetPrioirityClass来获取进程优先级。

14.通过命令行界面调用程序时,程序的起始优先级时normal。但是,如果使用START命令调用程序,可以使用一个开关指定程序的起始优先级。例如:

C:\WINDOWS\system32>START /LOW CALC.EXE


 15.CreateThread函数没有为调用者提供设置新线程相对优先级的办法。为了设置和获取线程的相对优先级,必须调用SetThreadPriority和GetThreadPriority函数。

16.CreateThread总是创建相对线程优先级为normal的新线程。要使线程以idle优先级执行,我们需要在调用CreateThead上传入CREATE_SUSPENDED标志,这将阻止线程执行任何代码。然后调用SetThreadPriority将线程改为idle相对线程优先级。接着调用ResumeThread,线程就成为可调度的了。

17.Windows并没有提供返回线程优先级的函数,因为Microsoft保留了任何时候改变调度算法的权力。

18.偶尔,系统也会提升一个线程的优先级,通常时为了响应某种I/O事件,比如窗口消息或磁盘读取。例如:

  • high优先级进程中的一个线程优先级为normal的线程,其基本优先级值为13.如果用户敲一个键,系统会在线程的队列中放入一个WM_KEYDOWN消息。因为有消息出现在线程的队列中,线程就成为可调度的了。而且,键盘设备驱动程序将使用系统临时提升线程的优先级。因此线程的优先级可能会提升2,从而达到15。
  • 线程在优先级为15时分得一个时间片。在该时间片结束之后系统将线程的优先级值减1,所以在下一个时间片中线程的优先级将为14.线程的第三个时间片以优先级13执行。以后的时间片将保持在13,即线程的基本优先级。

注意,线程的当前优先级不会低于线程的基本优先级。而且使线程可调度的设备驱动程序能够决定提升的幅度,同样,Microsoft也没有在文档中记录任何一个设备驱动程序能够将线程的优先级提升多少。因此,Microsoft可以不断地微调动态提升,以确定最佳的总体响应性。

19.系统只提升优先级值在1~15的线程。这个范围被称为动态优先级范围。而且,系统不会吧线程的优先级提升到实施范围(高于15)。系统也不能动态提升实时范围(16~31)的线程。

20.Microsoft增加SetProcessPriorityBoost和SetThreadPriorityBoost两个函数,允许我们禁止系统对线程优先级进行动态提升。GetProcessPriorityBoost和GetThreadPriorityBoost来查询。

21.当系统检测到有线程已经处于饥饿状态3到4秒,它会动态将饥饿线程的优先级提升到15,并允许该线程运行两个时间片。当两个时间片结束时,线程的优先级立即恢复到基本优先级。

22.如果用户需要使用某个进程的窗口,这个进程就称为前台进程,所有其他进程称为后台进程。为了改进前台进程的响应性,Windows会为前台进程中的现初微调调度算法。系统给前台进程的线程分配比一般情况下更多的时间片。这种微调只在前台进程是normal优先级时才进行。如果处于其他优先级,则不会进行微调。

23.从Windows Vista开始,线程可以在进行I/O请求时设置优先级。我们可以通过调研SetThreadPriority并传入THREAD_MODE_BACKGROUND_BEGIN来告诉Windows,线程应该发送低优先级的I/O请求。注意,这也将降低线程的CPU调度优先级。我们可以通过调用SetThreadPriority并传入THREAD_MODE_BACKGROUND_END,让线程进行normal优先级I/O的请求。系统不允许线程改变另一个线程的I/O优先级。

24.如果想让进程中的所有线程都进行低优先级的I/O请求和低CPU调度,那么我们可以调用SetPriorityClass,传入THREAD_MODE_BACKGROUND_BEGIN标志,相反则传入THREAD_MODE_BACKGROUND_END。

25.在更细的粒度上,normal优先级线程还可以执行对某个文件执行后台优先级I/O。例如SetFileInformationByHandle设置的优先级将覆盖进程的优先级或线程。

26.如果一个low优先级线程获得了normal优先级线程等待的锁,则normal优先级线程可以在低优先级I/O请求完成之前运行,不等待后台优先级线程。后台优先级线程甚至不再进行I/O,以免出现问题。因此,应该尽量减少在normal和后台优先级线程之间使用共享同步对象,以避免normal优先级线程为后台优先级线程拥有的锁而被阻塞,导致优先级逆转。

猜你喜欢

转载自www.cnblogs.com/zoneofmine/p/9869347.html