《操作系统》实验报告
实验题目 时间片轮转调度 学生姓名 lee 学 号
专业班级
指导教师
院系名称 计算机与信息学院
2017 年 10 月 30 日
实验6 时间片轮转调度
1. 实验目的与要求
??调试EOS的线程调度程序,熟悉基于优先级的抢先式调度。 ??为EOS添加时间片轮转调度,了解其它常用的调度算法。
2. 实验原理
阅读本书第5章中的第5.4节。重点理解EOS当前使用的基于优先级的抢先式调度,调度程
序执行的时机和流程,以及实现时间片轮转调度的细节。
3. 实验内容
3.1 准备实验
按照下面的步骤准备实验: 1. 启动OS Lab。
2. 新建一个EOS Kernel项目。
3.2 阅读控制台命令“rr”相关的源代码
阅读ke/sysproc.c文件中第690行的ConsoleCmdRoundRobin函数,及该函数用到的第649行的ThreadFunction函数和第642行的THREAD_PARAMETER结构体,学习“rr”命令是如何测试时间片轮转调度的。在阅读的过程中需要特别注意下面几点:
??在ConsoleCmdRoundRobin函数中使用ThreadFunction函数做为线程函数,新建了20个优先级为8的线程,做为测试时间片轮转调度用的线程。
??在新建的线程中,只有正在执行的线程才会在控制台的对应行(第0个线程对应第0行,第1个线程对应第1行)增加其计数,这样就可以很方便的观察到各个线程执行的情况。 ??控制台对于新建的线程来说是一种临界资源,所以,新建的线程在向控制台输出时,必须使用“关中断”和“开中断”进行互斥(参见ThreadFunction函数的源代码)。 ??由于控制台线程的优先级是24,高于新建线程的优先级 8,所以只有在控制台线程进入“阻塞”状态后,新建的线程才能执行。
??新建的线程会一直运行,原因是在ThreadFunction函数中使用了死循环,所以只能在ConsoleCmdRoundRobin函数的最后调用TerminateThread函数来强制结束这些新建的线程。
按照下面的步骤执行控制台命令“rr”,查看其在没有时间片轮转调度时的执行效果: 1. 按F7生成在本实验3.1中创建的EOS Kernel项目。 2. 按F5启动调试。
3. 待EOS启动完毕,在EOS控制台中输入命令“rr”后按回车。
命令开始执行后,观察其执行效果(如图1),会发现并没有体现“rr”命令相关源代码的设计意图。通过之前对这些源代码的学习,20个新建的线程应该在控制台对应的行中轮流地显示它们的计数在增加,而现在只有第0个新建的线程在第0行显示其计数在增加,说明只有
第0个新建的线程在运行,其它线程都没有运行。造成上述现象的原因是:所有20个新建线程的优先级都是8,而此时EOS只实现了基于优先级的抢先式调度,还没有实现时间片轮转调度,所以至始至终都只有第0个线程在运行,而其它具有相同优先级的线程都没有机会运行,只能处于“就绪”状态。
图1:没有时间片轮转调度时“rr”命令的执行效果
3.3调试线程调度程序
在为EOS添加时间片轮转调度之前,先调试一下EOS的线程调度程序
PspSelectNextThread函数,学习就绪队列、就绪位图以及线程的优先级是如何在线程调度程序中协同工作的,从而加深对EOS已经实现的基于优先级的抢先式调度的理解。 3.3.1 调试当前线程不被抢先的情况
正像图1中显示的,新建的第0个线程会一直运行,而不会被其它同优先级的新建线程或者低优先级的线程抢先。按照下面的步骤调试这种情况在PspSelectNextThread函数中处理的过程。
1. 结束之前的调试。
2. 在ke/sysproc.c文件的ThreadFunction函数中,调用fprintf函数的代码行(第680行)添加一个断点。 3. 按F5启动调试。
4. 待EOS启动完毕,在EOS控制台中输入命令“rr”后按回车。“rr”命令开始执行后,会在断点处中断。
5. 查看ThreadFunction函数中变量pThreadParameter->Y的值应该为0,说明正在调试的是第0个新建的线程。
图2 执行效果
6. 激活虚拟机窗口,可以看到第0个新建的线程还没有在控制台中输出任何内容,原因是fprintf函数还没有执行。
7. 激活OS Lab窗口后按F5使第0个新建的线程继续执行,又会在断点处中断。再次激活虚拟机窗口,可以看到第0个新建的线程已经在控制台中输出了第一轮循环的内容。可以多按几次F5查看每轮循环输出的内容。
图3 第一轮循环
通过之前的调试,可以观察到第0个新建的线程执行的情况。按照下面的步骤调试,查看当有中断发生从而触发线程调度时,第0个新建的线程不会被抢先的情况。 1. 在ps/sched.c文件的PspSelectNextThread函数中,调用BitScanReverse函数扫描就绪位图的代码行(第384行)添加一个断点。
2. 按F5继续执行,当有定时计数器中断发生时(每10ms一次),就会在新添加的断点处中断。
3. 在“调试”菜单的“窗口”中选择“监视”,激活“监视”窗口(此时按F1可以获得关于“监视”窗口的帮助)。
4. 在“监视”窗口中添加表达式“/t PspReadyBitmap”,以二进制格式查看就绪位图的值。此时就绪位图的值应该为100000001,表示优先级为8和0的两个就绪队列中存在就绪线程。(注意,如果就绪位图的值不是100000001,就继续按F5,直到就绪位图变为此值)。
5. 在“调试”菜单中选择“快速监视”,在“快速监视”对话框的“表达式”中输入表达式
“*PspCurrentThread”后,点击“重新计算”按钮,可以查看当前正在执行的线程(即被中断的线程)的线程控制块中各个域的值。其中优先级(Priority域)的值为8;状态(State域)的值为2(运行态);时间片(RemainderTicks域)的值为6;线程函数(StartAddr域)为ThreadFunction。综合这些信息即可确定当前正在执行的线程就是新建的第0个线程。关闭“快速监视”对话框。
图4 快速“监视”窗口
6. 在“监视”窗口中添加表达式“ListGetCount(&PspReadyListHeads[8])”,查看优先级为8的就
绪队列中就绪线程的数量,值为19。说明除了正在执行的第0个新建的线程外,其余19个新建的线程都在优先级为8的就绪队列中。ListGetCount函数在文件rtl/list.c中定义。 7. 按F10单步调试,BitScanReverse函数会从就绪位图中扫描最高优先级,并保存在变量HighestPriority中。查看变量HighestPriority的值为8。