Windows线程调度机制
Windows线程管理与调度机制

Windows线程管理与调度机制电机系电84班姓名:陆海学号:2008011001 在说Windows线程之前,必须简单谈一下Windows进程与线程的关系。
在Windows系统中,系统服务和所有的应用程序都是以进程的形式驻留在内存中的,由CPU调度执行来完成设定的运算功能。
简单来说,进程通常定义为一个正在运行的程序的实例。
然而,在Windows系统中,与Linux系统有很大不同,进程是不活泼的。
若要使进程完成某项操作,它必须至少拥有一个线程,该线程负责执行包含在进程的地址空间中的代码。
实际上每一个进程都包含一个或多个线程,Windows内核调度的对象是线程。
当创建一个进程时,系统会自动创建它的第一个线程,即主线程,然后,主线程能够创建子线程,子线程还能够继续创建子线程。
所有这些线程都“同时”执行进程地址空间中的代码。
线程是为了提高系统内程序的并发执行程度而提出来的概念,它是比进程更小的能独立运行的基本单位。
Windows的进程与线程是紧密相关的,系统通过创建进程来为线程提供必要的上下文环境,如内存、资源对象等。
线程通过线程环境块中的指针指向它属于的进程的进程环境块,因此线程调度器可以通过线程访问进程环境提供的上下文信息。
了解清楚了Windows中进程与线程的关系后,下面介绍一下线程的组成。
线程由两部分组成:一个是线程的内核对象,操作系统用它来对线程实施管理;另一个是线程堆栈,它用于维护线程在执行代码时需要的所有函数参数和局部变量。
因此,一个线程主要包含以下信息:惟一的线程标志;CPU寄存器的状态数据(用以表示处理器的状态);两个线程堆栈,一个在用户态执行时使用,一个在核心态执行时使用;一个供子系统,运行库和动态链接库使用的线程本地存储空间。
在Windows系统中,程序是通过调用相应的线程创建函数来创建一个新的线程的,最常见的线程创建函数是CreateThread。
CreateThread通过系统服务调用将创建线程的请求传递到位于Windows执行体中的进程管理器,由它创建线程对象,并调用相应的服务初始化核心线程块。
windows、linux调度

– 线程的基本优先级 = [进程的基本优先级 - 2,进程 [进程的基本优先级 的基本优先级 + 2],由应用程序控制 2],由应用程序控制 – 线程的动态优先级 = [进程的基本优先级 - 2, 31], [进程的基本优先级 31], 由windows2000核心控制 windows2000核心控制
Windows 2000线程调度-主动切换 2000线程调度线程 优先级 运行状态 就绪状态
转到 阻塞状态
抢先
线程 运行状态 优先级 就绪状态 从阻塞状态唤醒
可能在以下两种情况下出现抢先: 高优先级线程的等待完成。 一个线程的优先级被增加或减少。 用户态下运行的线程可以抢先内核态下运行的线程。 在判断一个线程是否被抢先时,并不考虑线程处于用户态还是内核态,调 度器只是依据线程优先级进行判断。 当线程被抢先时,它被放回相应优先级的就绪队列的队首。 处于实时优先级的线程在被抢先时,时间配额被重置为一个完整的时间片; 处于动态优先级的线程在被抢先时,时间配额不变,重新得到处理机使用 权后将运行到剩余的时间配额用完。
时间配额的控制
在系统注册库中的一个注册项 “HKLM\SYSTEM\CurrentControlSet\Control\PriorityC ontrol\Win32PrioritySeparation”,允许用户指定线程时 间配额的相对长度(长或短)和前台进程的进程的时间 配额是否加长。该注册项为6位,分成3个字段,每个 字段占2位。
Win32的线程调度应用编程接口 Win32的线程调度应用编程接口
• 进程优先级类函数:
– GetPriorityClass(读取) GetPriorityClass(读取) – SetPriorityClass(设置) SetPriorityClass(设置)
进程、线程调度模型及其在Windows2000中的实现

与内核级线程相对应的,是用户级线程。这类实现多见于一些历史悠久的操作系统(如Unix系列),为了在操作系统中加入线程支持,采用了在用户空间增加运行库来实现线程。这些运行库被称为“线程包”。
进程、线程调度模型及其在Windows2000中的实现
在传统的操作系统中,每个进程有一个自己的地址空间以及一个单一的控制流程。事实上,这几乎就是传统操作系统中进程的定义。
但是,现实中有很多情况下需要在同一个地址空间中完成并行的任务,比如Web服务器程序,虽然使用多进程方式编程也可以很好地实现服务器,但进程间的数据共享由于需要跨越地址空间而显得十分不方便,同时进程间切换的开销也不可小视。
其实,在用户等待磁盘操作完成的时候,虽然进程对用户的输入无响应,但CPU确实是空闲的(假定没有忙碌的后台进程),理论上CPU应该可以响应用户输入。这样,我们就回到了多任务系统的设计初衷:提高CPU利用率。
我们先来讨论两个不使用线程模型的解决方案:多进程编程和使用异步系统调用。
如果使用多进程方式,则由主进程新建一个工作进程,将需要保存的数据传递给工作进程以进行保存操作。如果需要保存的数据量非常大,内存间的数据复制是一个可观的开销。当然,在较新的操作系统如System V中,由于采用COW(Copy On Write)技术,这个性能损失可以略过。另一个改进办法是使用共享内存,在一些不使用fork方式新建进程的操作系统上这是个好办法。
这两条路那一条都不是很方便。问题的关键在于,同步对象的句柄值只是每个进程对象表中的索引,在另一个进程中是无效的。但在线程模型下,这个问题就迎刃而解了。因为(同一进程中的)线程间共享同一张内核对象表,所以同一个同步对象的句柄对各线程来说都是有效的,传递时只要直接传句柄值就行了。
计算机windows操作系统调度

9.0 9.2 0.2 1.0
4
9:10 0.1 0.1 9.2 9.3 0.2 2.0
2
• 任一调度算法要想同时满足上述目标是 不可能的:
1)如要想吞吐量大,调度算法就应选择那些 估计执行时间短的作业。这对那些估计执 行时间长的作业不公平,并且可能使它们 的得不到调度执行或响应时间很长。
2)如果考虑的因素过多,调度算法就会变得 非常复杂。其结果是系统开销增加,资源 利用率下降。
衡量调度策略的常用指标
• 周转时间:指将一个作业提交给计算机系统 后到该作业的结果返回给用户所需要的时间。
• 吞吐量:指在给定的时间内,一个计算机系 统所完成的总工作量(作业数)。
• 响应时间:指从用户向计算机发出一个命令 到计算机把相应的执行结果返回给用户所需 要的时间。
• 设备利用率:输入输出设备的使用情况,在 有些要求I/O处理能力强(如管理信息系统)的 系统中,高的设备利用率也是一个衡量调度 策略好坏的重要指标。
时间片轮转程序调度算法 (RR)
把CPU划分成若干时间片,并且按顺序赋给就绪 队列中的每一个进程,进程轮流占有CPU,当 时间片用完时,即使进程未执行完毕,系统 也剥夺该进程的CPU,将该进程排在就绪队列 末尾。同时系统选择另一个进程运行
本算法主要用于微观调度,说明怎样并发运行,即切换 的方式;设计目标是提高资源利用率。
2 短作业优先(Shortest Job first,SJF)方式
选择那些估计需要执行时间最短的作业投入执 行,为它们创建进程和分配资源。有可能使 得那些长作业永远得不到调度执行
3 响应比高者优先( Highest Response-ratio Next , HRN)方式
• 响应比R=(W+T)/T=1+W/T T:为估计需要的执行时间 W:在后备状态队列中的等待时间 T+W:响应时间
Windows操作系统的线程调度机制

Windows操作系统的线程调度机制2011010*** 热动** ***在操作系统的历史演变中,人们总是为了提高CPU的效率而一直努力着。
从串行处理到简单批处理、中断机制的引入,到多道程序设计批处理和分时系统,CPU的利用率便一直在提高,同时程序的执行的复杂性也日益增多。
随着多道程序操作系统的出现,怎样有序高效的管理程序的执行成为了当时人们关注的焦点。
为了程序执行的正确性和高效性,我们通常需要达到以下几个目的:1.资源对多个应用程序可见2.物理处理器能够在多个应用程序间切换保证每个程序都在执行中,即并发执行3.提高资源利用率和系统吞吐量。
在这些目的的推动下,“进程”这样作为能分配给处理器并由处理器执行的实体便氤氲而生了。
众所周知,一个进程既是资源分配单位,即每个进程拥有虚拟地址空间,保存进程映像,控制一些资源(文件,I/O设备),有状态、优先级等,也是CPU调度单位,即进程是一个程序的执行轨迹。
进程的这两个属性也是能够并发执行的基础。
当产生一个中断时,处理器保存当前执行进程的所有信息(进程控制块),调入中断处理程序,并继续到下一个指令周期,当中断处理结束后,处理器恢复刚刚被中断进程的所有信息和分配的资源,保证该进程可以从被中断处重新开始执行。
简单情形下,进程主要有三个状态:就绪态、运行态、阻塞态。
简单的轮转调度中,处于运行态的进程超时后,CPU产生时钟中断,将该进程转回就绪态,并重新调度另一个排队的就绪态进程进入运行态运行;当运行态的进程执行I/O或其他调用后,为了不让CPU做长时间的等待,该进程会被置为阻塞态,I/O准备完毕后该进程会重新进入就绪态进行排队。
采用这样的方式能够让所有的程序依次执行,由于CPU的速度很快、时间片的长度很短,看上去就像所有程序并发执行一样,同时CPU的利用率也会提高。
引入进程最开始的目的是为了程序并发执行、提高CPU利用率,但是由于前述的“进程是资源资源分配单位”,因而在创建、终止、切换过程中,系统必须为之付出较大的时空开销。
线程调度的三种方法

线程调度的三种方法
线程调度是操作系统中的重要概念之一,它指的是操作系统如何在多个线程之间分配处理器资源。
在实际的操作系统中,有三种常用的线程调度方法,分别是时间片轮转法、优先级调度法和多级反馈队列调度法。
时间片轮转法是一种基于时间片的调度方法。
操作系统将每个线程分配一个固定长度的时间片,当时间片用完后,操作系统会自动切换到下一个线程。
这种调度方法实现简单,能保证公平性,但是在高负载的情况下容易出现饥饿现象。
优先级调度法是一种基于线程优先级的调度方法。
每个线程都会被赋予一个优先级,优先级高的线程被优先调度。
这种调度方法可以保证高优先级线程的及时响应,但是如果优先级过于低的线程可能会被长期阻塞。
多级反馈队列调度法是一种综合了时间片轮转和优先级调度的
方法。
操作系统将线程按照优先级分成多个队列,并为每个队列分配不同的时间片。
当一个线程在一个队列中运行的时间超过了时间片,它就会被移到下一个队列中,直到运行完毕。
这种调度方法可以保证高优先级线程的及时响应,同时避免低优先级线程的饥饿现象。
总之,不同的线程调度方法适用于不同的场景,操作系统可以根据实际情况选择恰当的调度策略。
- 1 -。
windows线程切换

《Windows核心编程》读书心得——线程(线程调度)(4)l CPU切换调度线程:每个线程都有一个上下文(CONTEXT),保存在线程的内核对象中。
它记录了线程上一次执行时CPU寄存器的状态。
大约每隔20ms,CPU会查看当前所有的线程,然后在可调度的线程中选择一个进行调度。
CPU切换调度线程时,先将CPU寄存器写回线程上下文(这样,下次调度该线程,就能从上次停止的地方继续),停止运行原线程;检查剩下的可调度线程内核对象,选择另一个线程内核对象,将该线程上下文载入CPU寄存器。
查看和设置CONTEXT:查看context:GetThreadContext()设置context:SetThreadContext()l 线程的挂起和恢复:ResumeThread():将挂起计数减1,当挂起计数为0,唤醒线程。
(若调用成功,返回线程的前一个挂起计数;否则,返回0xFFFFFFFF)SuspendThread():将挂起计数加1,挂起线程。
Sleep():线程睡眠,让出时间来调度其他线程。
(假如需要强制CPU停止调度当前线程,把时间片让给其他线程,可以使用Sleep(0)或SwitchToThread(),两者的区别如下:Sleep():时间片只能让给优先级相同或更高的线程;SwitchToThread():只要有可调度线程,即便优先级较低,也会让其调度。
)l 两个从消息队列取消息的函数:PeekMessage():从消息队列取一条消息,但不取走。
GetMessage():从消息队列取一条消息,并取走。
l 线程优先级:优先级为0的线程:系统启动时,会创建一个优先级为0的“页面清零线程”,它只有在系统中没有其他可调度线程时,才能调度,用来清除内存中的闲置页面。
优先级在1 ~ 15之间的线程:一般用户模式下,线程的优先级都在该范围。
优先级在16 ~ 30之间的线程:一般是内核线程。
线程的抢占:较高优先级的线程,总是会抢占较低优先级线程的处理时间。
操作系统线程及线程调度

操作系统线程及线程调度本⽂是《go调度器源代码情景分析》系列第⼀章预备知识的第8⼩节。
要深⼊理解goroutine的调度器,就需要对操作系统线程有个⼤致的了解,因为go的调度系统是建⽴在操作系统线程之上的,所以接下来我们对其做⼀个简单的介绍。
很难对线程下⼀个准确且易于理解的定义,特别是对于从未接触过多线程编程的读者来说,要搞懂什么是线程可能并不是很容易,所以下⾯我们抛开定义直接从⼀个C语⾔的程序开始来直观的看⼀下什么是线程。
之所以使⽤C语⾔,是因为C语⾔中我们⼀般使⽤pthread线程库,⽽使⽤该线程库创建的⽤户态线程其实就是Linux操作系统内核所⽀持的线程,它与go语⾔中的⼯作线程是⼀样的,这些线程都由Linux内核负责管理和调度,然后go语⾔在操作系统线程之上⼜做了goroutine,实现了⼀个⼆级线程模型。
#include <stdio.h>#include <unistd.h>#include <pthread.h>#define N (1000 * 1000 * 1000)volatile int g=0;void* start(void*arg){int i;for(i=0; i<N; i++) {g++;}return NULL;}int main(int argc, char* argv[]){pthread_t tid;// 使⽤pthread_create函数创建⼀个新线程执⾏start函数pthread_create(&tid, NULL, start, NULL);for(;;) {usleep(1000*100*5);printf("loop g: %d\n", g);if(g==N) {break;}}pthread_join(tid, NULL); // 等待⼦线程结束运⾏return0;}该程序运⾏起来之后将会有2个线程,⼀个是操作系统把程序加载起来运⾏时创建的主线程,另⼀个是主线程调⽤pthread_create创建的start ⼦线程,主线程在创建完⼦线程之后每隔500毫秒打印⼀下全局变量 g 的值直到 g 等于10亿,⽽start线程启动后就开始执⾏⼀个10亿次的对g ⾃增加 1 的循环,这两个线程同时并发运⾏在系统中,操作系统负责对它们进⾏调度,我们⽆法精确预知某个线程在什么时候会运⾏。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Windows线程调度机制
线程是程序指令可被操作系统单独执行的最小单元,一个线程被包含在进程中,多线程可以存在于同一进程并分享进程的资源诸如内存等,然而进程却不可以。
在一个单独的进程中,多线程由操作系统将时间片段化依次执行,这一过程之快以至于让用户认为多个任务的线程在同时执行,当然,线程确实可以同时进行考虑到每个进程同时执行单独的线程。
一. 线程与进程的不同
线程与传统的多任务进程的操作系统不同之处在于:
1.进程独立而线程作为进程的一部分
2.进程比线程包括更多的状态信息,然而一个进程中的多线程共享这些信息,内存以及其他资源。
3.进程有分离的地址空间,而线程共享他们的地址空间
4.进程只能通过系统提供的内部交流机制沟通
5.在同一进程中线程的环境变换快于进程
二. 多线程
多线程是一个广泛的编程和执行模型,它允许多线程在同一进程环境,这些线程共享进程的资源,但却能够独立的执行。
线程的这一理念允许开发者对同时执行有了新的抽象。
多线程也可被应用于单独的进程以便实现在多进程的操作系统上完成平行执行的任务。
多线程使得拥有多CPU的电脑系统可以运行更快,因为程序的线程可将自己借出实现同时执行。
在这类情况下,程序需要注意避免冲突。
为了使数据被正确使用,线程需经常约定次序以便数据被以正确的顺序处理。
线程也可能需要相互的外部操作去避免共用的数据被同时修改或读取。
类似的粗心错误将导致死锁。
多线程的另一个即便是单CPU也能用得上的功能是有能力保持对外部输入保持响应。
在单线程的程序中,如果主要的执行线程在长任务中死了,整个应用就都瘫痪了。
但如果将这一任务移至一个与主线程同时执行的线程,那么这一应用就有可能对用户的输入做出反应。
另一方面,在普遍情况下,多线程并不是解决程序响应的唯一出路。
操作系统用以下一或两种方式调度线程:
1.抢先多任务处理被普遍认为是最好的处理方式,它允许操作系统决定什么时候进行环境变换。
但不足是抢先多任务处理可能在不合适的时间改变环境导致优先级翻转等一些不好的结果。
2.合作多任务处理依靠线程遇到断点自己放弃控制,这将在线程等待可用资源时出现问题。
直到90年代末,台式机的CPU仍然没有对多线程给予足够的支持,然而线程仍然被使用在这样的电脑上,因为线程之间的环境转换远快于进程上的环境转换,在嵌入式操作系统中的那些对实时行为有高要求的进程有可能通过减少线程转换的时间来支持多线程,这可能是通过为每个线程安排特定的注册表而非普通的注册表来实现。
最终,在90年代末尾,被称作同时多线程,即多线程同时执行数条指令通过英特尔奔腾4处理器在台式机完成,
三. 内核线程和用户线程
内核线程是内核调度中“最轻”的单元。
其建立和销毁都是由操作系统负责、通过系统调用完成。
操作系统在调度时,参考各进程内的线程运行情况做出调度决定,如果一个进程中没有就绪态的线程,那么这个进程也不会被调度占用CPU。
如果多内核线程可以同时存在于同一个进程,并且它们分享共同的内存和文件资源,那么内核线程在操作系统的进程是抢先多任务处理时也是抢先多任务处理的。
内核线程不拥有自己的资源除非是一个堆,即注册表的一份拷贝包括程序计数器和本地线程分配。
内核可以安排一个线程去系统中的每一个逻辑层面的核(因为每个支持多线程的进程将自己分为多个逻辑层面的核,或者在它不支持多线程的情况下在每一个物理上的核只支持一个逻辑核的运行),然后可以去除那些已经死掉的线程。
然而,内核线程比用户线程花费更多的时间
线程有时也被用在用户空间,这就叫做用户线程。
内核并不知道它们,所以它们被在用户空间执行和调度。
四.并发和数据结构
由于同一进程中的各线程分享相同的地址,这就允许同时运行多条代码,当线程间的数据结构被共享时,即便最简单的数据结构,由于他们要求超过一条的CPU 指令去更新也可能倾向于竞争:两个线程可能最终由于同时企图更新数据却未意识到原始数据已被更改而终止。
为防止这类情况的发生,线程的API函数提供同步原语去锁住数据结构以防两个线程可以同时得到。
在单处理机系统上,一个进入互斥体的线程必须等待并且触发一个环境改变。
在多进程系统中,线程可以以自旋锁代替互斥体。
五.I/O和调度
用户线程实现通常完全在用户空间。
其结果是,用户在同一进程中线程的环境切换是极其有效的,因为它可以通过在本地保存当前正在执行的用户线程所使用的CPU寄存器的环境切换,不要求在内核中的任何交互:然后由用户要执行的线程加载所需要的寄存器。
由于调度发生在用户空间,调度策略可以更容易地根据程序的工作量的要求变化。
然而,使用阻塞的系统调用在用户线程(而不是内核线程)可能会有问题。
如果用户线程或光纤进行系统调用块,其他用户线程和纤维的过程中都无法运行,直到系统调用返回。
总结:Windows调度单位是线程而不是进程,采用严格的抢先式动态优先级调度,依据优先级和分配时间片来调度。
每一个优先级的就绪进程排成一个先进先出队列,当一个线程状态变成就绪时,它可能立即运行或排到相应优先级队列的尾部,并且系统总运行优先级最高的就绪进程。
Windows在内核中实现线程调度代码,采用完全的事件驱动机制,在被抢先前没有保证的运行时间。
对于多处理机系统,Windows可以实现多个线程并行运行。