06时间片轮转调度Word下载.docx
《06时间片轮转调度Word下载.docx》由会员分享,可在线阅读,更多相关《06时间片轮转调度Word下载.docx(18页珍藏版)》请在冰豆网上搜索。
第0个新建的线程在运行,其它线程都没有运行。
造成上述现象的原因是:
所有20个新建
线程的优先级都是8,而此时EOS只实现了基于优先级的抢先式调度,还没有实现时间片轮转调度,所以至始至终都只有第0个线程在运行,而其它具有相同优先级的线程都没有机会运行,只能处于就绪”状态。
图1没有时间片轮转调度时rr"
命令的执行效果
调试线程调度程序
在为EOS^加时间片轮转调度之前,先调试一下EOS勺线程调度程序
PspSelectNextThread函数,学习就绪队列、就绪位图以及线程的优先级是如何在线程调度程序中协同工作的,从而加深对EOSB经实现的基于优先级的抢先式调度的理解。
3.3.1调试当前线程不被抢先的情况
正像图1中显示的,新建的第0个线程会一直运行,而不会被其它同优先级的新建线程或者低优先级的线程抢先。
按照下面的步骤调试这种情况在PspSelectNextThread函数中处
理的过程。
1.结束之前的调试。
2.在ke/文件的ThreadFunction函数中,调用fprintf函数的代码行(第680行)添加一个断点。
3.按F5启动调试。
4.待EOSB动完毕,在EO腔制台中输入命令rr”后按回车。
rr”命令开始执行后,会在
断点处中断。
5.查看ThreadFunction函数中变量pThreadParameter->
Y的值应该为0,说明正在调试的是第0个新建的线程。
图2执行效果
6.激活虚拟机窗口,可以看到第0个新建的线程还没有在控制台中输出任何内容,原因是
fprintf函数还没有执行。
7.激活OSLab窗口后按F5使第0个新建的线程继续执行,又会在断点处中断。
再次激活虚拟机窗口,可以看到第0个新建的线程已经在控制台中输出了第一轮循环的内容。
可以多按几次F5查看每轮循环输出的内容。
图3第一轮循环
通过之前的调试,可以观察到第0个新建的线程执行的情况。
按照下面的步骤调试,查
看当有中断发生从而触发线程调度时,第0个新建的线程不会被抢先的情况。
1.在ps/文件的PspSelectNextThread函数中,调用BitScanReverse函数扫描就绪位图的代码行(第384行)添加一个断点。
2.按F5继续执行,当有定时计数器中断发生时(每10ms一次),就会在新添加的断点处
中断。
3.在调试”菜单的窗口”中选择监视”激活监视”窗口(此时按F1可以获得关于监视窗口的帮助)。
4.在监视"
窗口中添加表达式/tPspReadyBitmap”以二进制格式查看就绪位图的值。
此时就绪位图的值应该为1,表示优先级为8和0的两个就绪队列中存在就绪线程。
(注意,
如果就绪位图的值不是1,就继续按F5,直到就绪位图变为此值)。
5.在调试”菜单中选择快速监视”在快速监视”对话框的表达式”中输入表达式*PspCurrentThread"
后,点击重新计算"
按钮,可以查看当前正在执行的线程(即被中断的线程)的线程控制块中各个域的值。
其中优先级(Priority域)的值为8;
状态(State域)的值为2(运行态);
时间片(RemainderTicks域)的值为6;
线程函数(StartAddr域)为ThreadFunction。
综合这些信息即可确定当前正在执行的线程就是新建的第0个线
程。
关闭快速监视”对话框。
快速监视
Priority=01x8,
State=QxZfRemainderTicIci=Oxt^V/aitStatus=DxO」V/aitTimer—{
IntervalTicks=(Mi』ElapsedTicks=dx*03fc40c
TimerRjDutine=0xB03Fc"
HOCjParameter=OxBOSfcSfO,TimerListEntry—{
Next=(KO』
Pre^=OwO
StateListErttry=-[
Next=0x0,Prev—Oxfl
h
V/aitU5tHMcl=-I
Next—0x&
03Fc42Sj
Prev=0a8(J3fc42S
}」
temelStack=0xa00030&
0j
Kernel匚cintEud:
=彳
E^x—OxaOOD'
tfe^j
Ecx=OxOj
Edx—0x1j
Ebx=OxOjEsp=(b(a0004(c0j
Ebp=OxaDOD4Fe6j
Esi=Ox》
Edi=MJ
Et)=0x30018c7+?
EFI^q=0x206,SegCs=0x0;
ScqSs=OkIOj
5和氐=CM山
ScgEs=Ckiq
ScqFs=0x1Qj
SegGs=0x10
};
AttachedPas=0x30024^1^
StartAddr=D^a0013bf3<
ThreadFijnction>
j
图4快速监视”窗口
6.在监视"
窗口中添加表达式ListGetCount(&
PspReadyListHeads[8])”,查看优先级为
8的就
绪队列中就绪线程的数量,值为19。
说明除了正在执行的第0个新建的线程外,其余19个
新建的线程都在优先级为8的就绪队列中。
ListGetCount函数在文件rtl/中定义。
7.按F10单步调试,BitScanReverse函数会从就绪位图中扫描最高优先级,并保存在变量
HighestPriority中。
查看变量HighestPriority的值为8。
+甲x
值
无弦计算表起式的值-
100000001
0k13
0k8
图5监视”窗口
8.继续按F10单步调试,直到在PspSelectNextThread函数返前停止(第465行),注意观察线程调度执行的每一个步骤。
第0个新建的线程在执行线程调度时没有被抢先的原因可以归纳为两点:
(1)第0个新建的线程仍然处于运行”状态。
(2)没有比其优先级更高的就绪线程。
332调试当前线程被抢先的情况
如果有比第0个新建的线程优先级更高的线程进入就绪状态,则第0个新建的线程就会被
抢先。
按照下面的步骤调试这种情况在PspSelectNextThread函数中处理的过程(注意,接下来的调试要从本实验3.3.1调试的状态继续调试,所以不要结束之前的调试)。
1.选择调试”菜单中的删除所有断点”删除之前添加的所有断点。
2.在ps/文件的PspSelectNextThread函数的第395行添加一个断点。
3.按F5继续执行,激活虚拟机窗口,可以看到第0个新建的线程正在执行。
4.在虚拟机窗口中按下一次空格键,EOS^在之前添加的断点处中断。
5.在监视”窗口中查看就绪位图的值为0001,说明此时在优先级为24的就绪队列中存在
就绪线程。
在监视"
PspReadyListHeads[24])”其值
为1,说明优先级为24的就绪队列中只有一个就绪线程。
扫描就绪位图后获得的最高优先级的值HighestPriority也就应该是24。
图6监视”窗口就绪位图为0001
6.按F10单步调试一次,执行的语句会将当前正在执行的第0个新建的线程,放入优先
级为8的就绪队列的队首。
监视”窗口中显示的优先级为8的就绪队列中的线程数量就会增加1,变为20。
图7监视”窗口线程数量
7.继续按F10单步调试,直到在第444行中断执行,注意观察线程调度执行的每一个步骤。
此时,正在执行的第0个新建的线程已经进入了就绪”状态,让出了CPU线程调度程序接
下来的工作就是选择优先级最高的非空就绪队列的队首线程作为当前运行线程,也就是让
优先级为24的线程在CPUh执行。
8.按F10单步调试一次,当前线程PspCurrentThread指向了优先级为24的线程。
可以在快速监视"
窗口中查看表达式*PspCurrentThread"
的值,注意线程控制块中StartAddr域的值为IopConsoleDispatchThread函数(在文件io/中定义),说明这个优先级为24的线程是控制台派遣线程。
表这式
+PspCurrentThre-ad
|重新计复(也
麻加监视(世)
Process=CxfiOSfclfO,
Thre-adListErttry=-[
Mexlt—0x803ficef4,
Prev=0txS03k2f4
Priorty=0x18,
5tete=Oxlf
RemainderTicks=OzE^
WaitStatLE=0x0,
WeitTirrier=-[
IntervalTicl=5=0>
;
1,
ElapsedTkks=0x0,
TimerPiQUtine=Qj
P^remeter=DxO,TimerListEntry={
=Q^j
Prev=0x0
I
St^teListEntry-■{
Next-OxSOOEWaj
Prav=0x80024698
WaitiistHedd={
N8xt=Ox803Fcb28,
pw=Cteaasfcfazfi
K&
rnelStack=QxaODOSOaOj
KerndContsxt=<
Eax■0x603fcafOj
Ectf・iO妬OG證2幽』
Edx-OxSCDfceaCj
Fbx■0x0^
Bp■O^aQOOGtdO,
Ebp=OxaOOOfieee,
凶■0x0.
Edi■OkO’
Eip■OxSOOleSeSj
EFhg■0x2^
SegCs三OjcB,
SegSs=OjcIOj
Segps=OxiD,
5egE#-0^10,
SegFs-OxilO,
虫3^苗=0
Attach&
dPas=0x8002451缶
StartAddr=0^60015&
95^IapConsoleDispatehThready
图9监视”窗口
10.删除所有断点后结束调试。
为E0躱加时间片轮转调度
341要求
修改ps/文件中的PspRoundRobin函数(第337行),在其中实现时间片轮转调度算法。
代码如下:
VOID
PspRoundRobin(
)
/*++
功能描述:
时间片轮转调度函数,被定时计数器中断服务程序KilsrTimer调用。
参数:
无。
返回值:
--*/
{
342代码修改完毕后,按F7生成EO胴核项目。
10。
3•在EO腔制台中输入命令rr”后按回车。
应能看到20个线程轮流执行的效果,如图
ActioilElitCDElcppyHelp
图10:
进行时间片轮转调度时rr”命令的执行效杲
3.4.3提示
PspRoundRobin函数具体的流程可以参考图5-11。
在PspRoundRobin函数中,全局变量PspCurrentThread指向的线程控制块就是被定时计数器中断的线程的线程控制块,通过对PspCurrentThread指向的线程控制块的各个域进行修
改,就可以改变被中断线程的各种属性。
全局变量PspCurrentThread的定义参见ps/的第45
行。
线程控制块结构体的定义参见ps/的第58行。
被中断线程的状态有可能不是“运行”状态,而是“阻塞”状态。
所以,在进行时间片轮转调度前,要先判断一下被中断线程是否仍处于“运行”状态。
只有当被中断线程仍处于
“运行”状态时,才需要进行时间片轮转调度。
在PspRoundRobin函数中的第一行代码可以
如下(线程状态的定义可以参见ps/的第93行):
if(NULL!
=PspCurrentThread&
&
Running
==PspCurrentThread->
State){在OSLab的“项目管理器”窗口中找到ps/文件,双击打
开此文件。
2.将ps/第104行定义的TICKS_OF_TIME_SLIC的值修改为1。
3.在EO腔制台中输入命令“rr”后按回车。
观察执行的效果。
4.按F7生成EO胴核项目。
5.按F5启动调试。
6.
图11线程示意
还可以按照上面的步骤为TICKS_OF_TIME_SLIC取一些其它的极端值,例如20或100等,分另U
观察“rr”命令执行的效果。
通过分析造成执行效果不同的原因,理解时间片的大小对时间
片轮转调度造成的影响。
(规律:
将TICKSOFTIMESLICED100时依次出现再循环依次出现)
(PressCtrl+Fl"
F8tasuitchconsol?
uindou+n.)⑻:
11S34
Thread1Thread2Thread3
图12时间片的大小对时间片轮转调度造成的影响。
4.思考与练习
1.结合线程调度执行的时机,说明在ThreadFunction函数中,为什么可以使用关中断"
和
开中断”的方法来保护控制台这种临界资源。
一般情况下,应该使用互斥信号量(MUTEX来保护临界资源,但是在ThreadFunction函数中却不能使用互斥信号量,而只能使用关中断”和开中断”的方法,结合线程调度的对象说明这样做的原因。
答:
关中断后CPU就不会响应任何由外部设备发出的硬中断(包括定时计数器中断和键盘中断等)了,也就不会发生线程调度了,从而保证各个线程可以互斥的访问控制台。
这里绝对不能使用互斥信号量(mutex)保护临界资源的原因:
如果使用互斥信号量,
则那些由于访问临界区而被阻塞的线程,就会被放入互斥信号量的等待队列,就不会在相应优先级的就绪列中了,而时间轮转调度算法是对就绪队列的线程进行轮转调度,而不是对这些被阻塞的线程进行调度,也就无法进行实验了。
使用“关中断”和“开中断”
进行同步就不会改变线程的状态,可以保证那些没有获得处理器的线程都在处于就绪队
列中。
2.时间片轮转调度发现被中断线程的时间片用完后,而且在就绪队列中没有与被中断线程
优先级相同的就绪线程时,为什么不需要将被中断线程转入就绪”状态?
如果此时将被
中断线程转入了就绪"
状态又会怎么样?
可以结合PspRoundRobin函数和PspSelectNextThread函数的流程进行思考,并使用抢先和不抢先两种情况进行说明。
因为由PspRoundRobin函数和PspSelectNextThread函数的流程可知,当时间片
轮转调度发现被中断线程的时间片用完后,而且在就绪队列中没有与被中断线程优先级
相同的就绪线程时,PspRoundRobin函数会直接结束,所以不需要将被中断线程转入“就绪”状态。
如果此时将被中断线程转入了“就绪”状态,那么比该中断线程更高的就绪
进程就无法运行。
3.在EOS只实现了基于优先级的抢先式调度时,同优先级的线程只能有一个被执行。
当实现了时间片轮转调度算法后,同优先级的线程就能够轮流执行从而获得均等的执行机会。
但是,如果有高优先级的线程一直占用CPU低优先级的线程就永远不会被执行。
尝试修改ke/文件中的ConsoleCmdRoundRobin函数来演示这种情况(例如在20个优先级为8的线程执行时,创建一个优先级为9的线程)。
设计一种调度算法来解决此问题,让
低优先级的线程也能获得被执行的机会。
解决该问题的最简单的方法是实现动态优先级算法。
动态优先级是指在创建进程时所赋
予的优先级,可以随线程的推进而改变,以便获得良好的调度性能。
例如,可用规定,在就绪队列中的线程,随着其等待时间的增长,其优先级以速率X增加,并且正在执行的线程,
其优先级以速率y下降。
这样,在各个线程具有不同优先级的情况下,对于优先级低的线程,
在等待足够的时间后,其优先级便可能升为最高,从而获得被执行的机会。
此时,在基于优
先级的抢占式调度算法、时间片轮转调度算法和动态优先级算法的共同作用下,可防止一个
高优先级的长作业长期的垄断处理器。
4.EOS内核时间片大小取60ms(和Windows操作系统完全相同),在线程比较多时,就可以观察出线程轮流执行的情况(因为此时一次轮转需要60ms,20个线程轮流执行一次需要60
X20=1200ms,也就是需要1秒多的时间,所以EOS勺控制台上可以清楚地观察到线程轮流执行的情况)。
但是在Windows、Linux等操作系统启动后,正常情况下都有上百个线程在并发执行,为什么觉察不到它们被轮流执行,并且每个程序都运行的很顺利呢?
在Windows、linux等操作系统中,虽然都提供了时间片轮转调度算法却很少真正被派上用场,下面解释原因,在Windows任务管理器中,即使系统中已经运行了数百个线程,
但CPU勺利用率仍然很低,甚至为0.因为这些线程在大部分时间都处于阻塞状态,阻塞的原因是各种各样的,最主要的原因是等待I/O完成或者等待命令消息的到达。
例如,在编辑
Word文档时,每敲击一次键盘,Word就会立即作出反应,并且文档中插入字符。
此时会感觉
Word运行的非常流畅。
事实上,并非如此,Wore主线程大部分时间都处于阻塞等待状态,等
待用户敲击键盘。
在用户没有敲击键盘或没有使用鼠标点击时,Word主线程处于阻塞状态,
它将让出处理器给其它需要的线程。
当用户敲击一个按键后,Word主线程将会立刻被操作系
统唤醒,此时Word开始处理请求。
Word在处理输入请求时所用的CPU寸间是非常短的(因为CPI非常快),是微秒级的,远远低于时间片轮转调度的时间片大小(Windows下是60毫秒),
处理完毕后Word又立刻进入阻塞状态,等待用户下一次敲击键盘。
或者拿音乐播放器来分析,表面上感觉播放器在不停地播放音乐,但是CPU勺利用率仍然会很低。
这是由于播放器将一
段声音编码交给声卡,由声卡来播放,在声卡播放完这段声音之前,播放器都是处于阻塞等
待状态的。
当声卡播放完片段后,播放器将被唤醒,然后它将下一个声音片段交给声卡继续播放。
掌握了上面的知识后,就可以很容易解释为什么这么多线程同时在运行而一点都感觉不到轮替现象。