定义表示给线程分配的时钟滴答(Tick)数量,多个时钟滴答组成了线程的时间片。时钟滴答的大小是由定时计数器中断的频率确定的,目前定时计数器每秒钟发生100次中断,则每个时钟滴答的大小是10ms,而且TICKS_OF_TIME_SLICE定义的值为6,所以时间片的大小就是60ms。
??在判断就绪队列中是否存这样的就绪线程,即其优先级与被中断线程优先级相同时,只需要扫描就绪位图即可(这样速度更快)。例如被中断线程优先级为8,则只需要判断就绪位图的第8位是否为1,为1则说明就绪队列中存在优先级为8的就绪线程,为0则说明不存在。此时,可以使用下面的代码作为条件语句中的布尔表达式: BIT_TEST(PspReadyBitmap, PspCurrentThread->Priority)
BIT_TEST是一个宏定义函数,其定义参见inc/eosdef.h的第220行。如果存在和被中断线程优先级相同的就绪线程,此函数返回非0(TURE),否则返回0(FALSE)。变量PspReadyBitmap是32位就绪位图,其定义参见ps/sched.c的第29行。
??可以使用下面的代码将被中断线程转入就绪状态,并将其插入对应优先级就绪队列的末尾: PspReadyThread(PspCurrentThread); 函数PspReadyThread的定义参见ps/sched.c的第107行。
3.5 修改线程时间片的大小
在成功为EOS添加了时间片轮转调度后,可以按照下面的步骤修改时间片的大小: 1. 在OS Lab的“项目管理器”窗口中找到ps/psp.h文件,双击打开此文件。 2. 将ps/psp.h第104行定义的TICKS_OF_TIME_SLICE的值修改为1。 3. 在EOS控制台中输入命令“rr”后按回车。观察执行的效果。 4. 按F7生成EOS内核项目。 5. 按F5启动调试。
图11 线程示意
还可以按照上面的步骤为TICKS_OF_TIME_SLICE取一些其它的极端值,例如20或100等,分别观察“rr”命令执行的效果。通过分析造成执行效果不同的原因,理解时间片的大小对时间片轮转调度造成的影响。
(规律:将TICKS_OF_TIME_SLICE取100时依次出现再循环依次出现)
图12 时间片的大小对时间片轮转调度造成的影响。
4. 思考与练习
1. 结合线程调度执行的时机,说明在ThreadFunction函数中,为什么可以使用“关中断”和
“开中断”的方法来保护控制台这种临界资源。一般情况下,应该使用互斥信号量(MUTEX)来保护临界资源,但是在ThreadFunction函数中却不能使用互斥信号量,而只能使用“关中断”和“开中断”的方法,结合线程调度的对象说明这样做的原因。
答:关中断后CPU就不会响应任何由外部设备发出的硬中断(包括定时计数器中断和键盘中断等)了,也就不会发生线程调度了,从而保证各个线程可以互斥的访问控制台。这里绝对不能使用互斥信号量(mutex)保护临界资源的原因:如果使用互斥信号量,则那些由于访问临界区而被阻塞的线程,就会被放入互斥信号量的等待队列,就不会在相应优先级的就绪列中了,而时间轮转调度算法是对就绪队列的线程进行轮转调度,而不是对这些被阻塞的线程进行调度,也就无法进行实验了。使用“关中断”和“开中断”进行同步就不会改变线程的状态,可以保证那些没有获得处理器的线程都在处于就绪队列中。
2. 时间片轮转调度发现被中断线程的时间片用完后,而且在就绪队列中没有与被中断线程
优先级相同的就绪线程时,为什么不需要将被中断线程转入“就绪”状态?如果此时将被中断线程转入了“就绪”状态又会怎么样?可以结合PspRoundRobin函数和
PspSelectNextThread函数的流程进行思考,并使用抢先和不抢先两种情况进行说明。 答:因为由PspRoundRobin 函数和 PspSelectNextThread 函数的流程可知,当时间片轮转调度发现被中断线程的时间片用完后,而且在就绪队列中没有与被中断线程优先级相同的就绪线程时,PspRoundRobin 函数会直接结束,所以不需要将被中断线程转入“就绪”状态。如果此时将被中断线程转入了“就绪”状态,那么比该中断线程更高的就绪进程就无法运行。
3. 在EOS只实现了基于优先级的抢先式调度时,同优先级的线程只能有一个被执行。当实
现了时间片轮转调度算法后,同优先级的线程就能够轮流执行从而获得均等的执行机会。但是,如果有高优先级的线程一直占用CPU,低优先级的线程就永远不会被执行。尝试修改ke/sysproc.c文件中的ConsoleCmdRoundRobin函数来演示这种情况(例如在20个优先级为8的线程执行时,创建一个优先级为9的线程)。设计一种调度算法来解决此问题,让低优先级的线程也能获得被执行的机会。 答:
解决该问题的最简单的方法是实现动态优先级算法。动态优先级是指在创建进程时所赋予的优先级,可以随线程的推进而改变,以便获得良好的调度性能。例如,可用规定,在就绪队列中的线程,随着其等待时间的增长,其优先级以速率X增加,并且正在执行的线程,其优先级以速率y下降。这样,在各个线程具有不同优先级的情况下,对于优先级低的线程,在等待足够的时间后,其优先级便可能升为最高,从而获得被执行的机会。此时,在基于优先级的抢占式调度算法、时间片轮转调度算法和动态优先级算法的共同作用下,可防止一个高优先级的长作业长期的垄断处理器。
4. EOS内核时间片大小取60ms(和Windows操作系统完全相同),在线程比较多时,就可以观察出线程轮流执行的情况(因为此时一次轮转需要60ms,20个线程轮流执行一次需要60×20=1200ms,也就是需要1秒多的时间,所以EOS的控制台上可以清楚地观察到线程轮流执行的情况)。但是在Windows、Linux等操作系统启动后,正常情况下都有上百个线程在并发执行,为什么觉察不到它们被轮流执行,并且每个程序都运行的很顺利呢?
答:在Windows、linux等操作系统中,虽然都提供了时间片轮转调度算法却很少真正被派上用场,下面解释原因,在Windows任务管理器中,即使系统中已经运行了数百个线程, 但CPU的利用率仍然很低,甚至为0.因为这些线程在大部分时间都处于阻塞状态,阻塞的原因是各种各样的,最主要的原因是等待I/O完成或者等待命令消息的到达。例如,在编辑Word文档时,每敲击一次键盘,Word就会立即作出反应,并且文档中插入字符。此时会感觉Word运行的非常流畅。事实上,并非如此,Word主线程大部分时间都处于阻塞等待状态,等待用户敲击键盘。在用户没有敲击键盘或没有使用鼠标点击时,Word主线程处于阻塞状态,它将让出处理器给其它需要的线程。当用户敲击一个按键后,Word主线程将会立刻被操作系统唤醒,此时Word开始处理请求。Word在处理输入请求时所用的CPU时间是非常短的(因为CPU非常快),是微秒级的,远远低于时间片轮转调度的时间片大小(Windows下是60毫秒),处理完毕后Word又立刻进入阻塞状态,等待用户下一次敲击键盘。或者拿音乐播放器来分析,表面上感觉播放器在不停地播放音乐,但是CPU的利用率仍然会很低。这是由于播放器将一段声音编码交给声卡,由声卡来播放,在声卡播放完这段声音之前,播放器都是处于阻塞等待状态的。当声卡播放完片段后,播放器将被唤醒,然后它将下一个声音片段交给声卡继续播放。掌握了上面的知识后,就可以很容易解释为什么这么多线程同时在运行而一点都感觉不到轮替现象。