09_实验八_拓展实验三
拓展实验三:线程调度算法改进
实验目的
实现多级反馈队列调度算法
实验步骤
实现时间片轮转调度算法。
修改时间片的大小 TICKS_OF_TIME_SLICE 为 100,方便观察执行后的效果。
在控制台命令“rr”的处理函数中,将 Sleep 时间更改为 200*1000,这样可以有充足的时间查看优先级降低后的效果。
修改线程控制块(TCB)结构体,在其中新增两个成员,一个是线程整个生命周期中合计使用的时间片数量,另一个是线程的初始时间片数量。
修改“rr”命令在控制台输出的内容和格式,不再显示线程计数,而是显示线程初始化时间片的大小,已使用时间片的合计数量,剩余时间片的数量。注意,在调用fprintf 函数格式化字符时,需要在字符串的末尾增加一个空格,否则会导致输出异常。
在实现多级反馈队列调度算法后(注意:数字越大,优先级越高,反之,数字越小,优先级越低),使用实验 6 中提供的“rr”命令,查看各个线程的优先级逐步降低的过程。
由于 EOS 没有提供鼠标,可以使用键盘事件或者控制台命令使线程优先级提升。由于键盘事件与线程之间没有建立一个明确的会话关系,所以还需要解决使用键盘事件提升哪个线程优先级的问题。一个简单的方式是,在键盘的中断处理程序中(在io/driver/keyboard.c 文件的 396 行的 KbdIsr 函数),如果当前线程(注意不能是 2 号线程)处于运行状态并且优先级大于 0 小于 8 的话(由于空闲线程的优先级为 0,不能更改该线程的优先级,如果当前线程的优先级为 8,没有必要再做提升线程优先级的操作),按下空格键,响应键盘事件后,就将其优先级提升为默认的优先
级即可。关于键盘中断相关的内容可以参考实验 12。
使用控制台命令提升线程优先级,在 EOS 操作系统中实现一个“up ThreadID”命令,通过输入的线程 ID 来提升对应线程的优先级。在实现命令的过程中需要做如下判断:需要提升线程的优先级应该大于 0 并且小于 8,如果是处于就绪状态的线程,需要先将该线程移出队列,然后设置该线程的优先级为默认值 8,并设置线程的初始时间片大小和剩余时间片大小,如果是处于运行状态或阻塞状态的线程,直接设置线程的优先级即可。测试提升线程优先级命令的方法:在控制台窗口 1 执行 rr时,可以按 Ctrl + F2 切换到控制台窗口 2,然后输入“up 24”命令,按回车执行该命令,按 Ctrl + F1 切换到控制台窗口 1,可以查看 ID 为 24 的线程优先级已
实验过程
实现时间片轮转调度算法
在sched.c文件342行
if (NULL != PspCurrentThread && Running == PspCurrentThread->State) {
// 在此实现时间片轮转调度算法
PspCurrentThread->RemainderTicks--;
// 被中断线程的剩余时间片等于 0
if (PspCurrentThread->RemainderTicks == 0) {
// 重新为被中断线程分配时间片
PspCurrentThread->RemainderTicks = TICKS_OF_TIME_SLICE;
// 存在和被中断线程优先级相同的就绪线程
if (BIT_TEST(PspReadyBitmap, PspCurrentThread->Priority) != 0) {
PspReadyThread(PspCurrentThread);
}
}
}
return;
}
修改时间片的大小 TICKS_OF_TIME_SLICE 为 100,方便观察执行后的效果。
#define TICKS_OF_TIME_SLICE 100
将 ps/psp.h 的 TICK_OF_TIME_SLICE 设置为 100,方便观察执行后的效果。
效果:
在控制台命令“rr”的处理函数中,将 Sleep 时间更改为 200*1000,这样可以有充足的时间查看优先级降低后的效果。
// 当前线程等待一段时间。由于当前线程优先级 24 高于新建线程的优先级 8,
// 所以只有在当前线程进入“阻塞”状态后,新建的线程才能执行。
Sleep(200 * 1000);
修改线程控制块(TCB)结构体,在其中新增两个成员
一个是线程整个生命周期中合计使用的时间片数量,另一个是线程的初始时间片数量。
// 添加的成员
ULONG LifeTimeTicks; // 线程整个生命周期的时间片
ULONG InitTicks; // 线程初始时间片数量
ULONG UesdTicks; // 已使用的时间片
修改“rr”命令在控制台输出的内容和格式
不再显示线程计数,而是显示线程初始化时间片的大小,已使用时间片的合计数量,剩余时间片的数量。注意,在调用 fprintf 函数格式化字符时,需要在字符串的末尾增加一个空格,否则会导致输出异常。
ULONG i;
UCHAR Priority;
COORD CursorPosition;
PTHREAD_PARAMETER pThreadParameter = (PTHREAD_PARAMETER)Param;
// 根据线程参数设置输出内容显示的位置。
CursorPosition.X = 0;
CursorPosition.Y = pThreadParameter->Y;
// 设置时间片
PspCurrentThread->LifeTimeTicks = TICKS_OF_TIME_SLICE * 12;
PspCurrentThread->InitTicks = TICKS_OF_TIME_SLICE;
PspCurrentThread->UesdTicks = 0;
// 在线程参数指定的行循环显示线程执行的状态。死循环。通过开关中断互斥访问控制台。
// 格式:Thread 序号 (优先级): 执行计数
for (i = 0; ; i++) {
__asm("cli");
PsGetThreadPriority(CURRENT_THREAD_HANDLE, &Priority);
SetConsoleCursorPosition(pThreadParameter->StdHandle, CursorPosition);
fprintf(pThreadParameter->StdHandle,
"Thread ID:%d, Priority:%d, InitTicks:%d, UsedTicks:%d, RemainderTicks:%d ",
ObGetObjectId(PspCurrentThread),
Priority,
PspCurrentThread->InitTicks,
PspCurrentThread->UesdTicks,
PspCurrentThread->RemainderTicks);
__asm("sti");
}
return 0;
实现多级反馈队列调度算法
VOID
PspRoundRobin(
VOID
)
// 时间片轮转调度函数,被定时计数器中断服务程序 KiIsrTimer 调用。
{
if (NULL != PspCurrentThread && Running == PspCurrentThread->State) {
PspCurrentThread->RemainderTicks--; // 当前运行线程的剩余时间减1
PspCurrentThread->UesdTicks++; // 当前运行线程已使用的时间片加1
// 线程生命周期结束
if(PspCurrentThread->UesdTicks == PspCurrentThread->LifeTimeTicks) {
PspCurrentThread->State = Terminated;
PspThreadSchedule();
return;
}
if (PspCurrentThread->RemainderTicks == 0) {
PspCurrentThread->RemainderTicks = TICKS_OF_TIME_SLICE;
//多级反馈队列核心
if(PspCurrentThread->Priority > 0) {
PspCurrentThread->Priority--;
PspCurrentThread->InitTicks += TICKS_OF_TIME_SLICE;
PspCurrentThread->RemainderTicks = PspCurrentThread->InitTicks;
PspThreadSchedule();
}
if (BIT_TEST(PspReadyBitmap, PspCurrentThread->Priority) != 0) {
PspReadyThread(PspCurrentThread);
}
}
}
return;
}
效果:
使用键盘事件或者控制台命令使线程优先级提升
在 io/driver/keyboard 文件的 396 行的 KbdIsr 函数)实现 按下VK_RSHIFT提升当前进程的优先级。
/* 0x36 */ VK_RSHIFT,
// 用空格键提高当前进程优先级
if(KeyEventRecord.VirtualKeyValue == VK_RSHIFT &&
PspCurrentThread->State == Running &&
PspCurrentThread->Priority > 0 &&
PspCurrentThread->Priority < 8)
PspCurrentThread->Priority = 8;
使用控制台命令提升线程优先级
在 ke\sysproc.c 文件开头添加控制命令 up 的函数定义
ConsoleCmdUp
PRIVATE
VOID
ConsoleCmdUp(
IN HANDLE StdHandle,
IN PCSTR Arg
);
KiShellThread函数中增加对“up“的判定
else if (0 == stricmp(Line, "up")) {
ConsoleCmdUp(StdHandle, Arg);
continue;
}
ConsoleCmdUp 的具体实现
PRIVATE
VOID
ConsoleCmdUp(
IN HANDLE StdHandle,
IN PCSTR Arg
)
/*++
功能描述:
提升线程优先级。控制台命令“up”。
参数:
StdHandle -- 标准输入、输出句柄。
Arg -- 命令参数字符串,需要被恢复的线程ID
返回值:
无。
--*/
{
BOOL IntState;
ULONG ThreadID;
PTHREAD pThreadCtrlBlock;
PMMVAD_LIST pVadList;
PLIST_ENTRY pListEntry;
PMMVAD pVad;
ULONG Index, TotalVpnCount, AllocatedVpnCount, FreeVpnCount, VpnCount;
STATUS Status;
//
// 从命令参数字符串中获得线程 ID。
//
ThreadID = atoi(Arg);
if(0 == ThreadID) {
fprintf(StdHandle, "Please input a correct thread ID.\n");
return;
}
//
// 由进程 ID 获得进程控制块
//
Status = ObRefObjectById(ThreadID, PspThreadType, (PVOID*)&pThreadCtrlBlock);
if (!EOS_SUCCESS(Status)) {
fprintf(StdHandle, "%d is an invalid process ID.\n", ThreadID);
return;
}
// 需要提升线程的优先级应介于0~8
if(pThreadCtrlBlock->Priority <= 0 || pThreadCtrlBlock->Priority >= 8){
fprintf(StdHandle, "Please input a valid thread ID which priority should be between 0 and 8.\n");
return;
}
IntState = KeEnableInterrupts(FALSE); // 关中断
// 如果是处于运行状态或阻塞状态,直接设置优先级。
if(pThreadCtrlBlock->State == Running || pThreadCtrlBlock->State == Waiting){
pThreadCtrlBlock->Priority = 8;
}
// 如果是处于就绪状态的线程,需要先将该线程移出队列,
// 然后设置该线程的优先级为8,恢复线程的初始时间片大小和剩余时间片大小
else if(pThreadCtrlBlock->State == Ready){
ListRemoveEntry(&pThreadCtrlBlock->StateListEntry);
pThreadCtrlBlock->State = Zero; // 设置为游离状态
pThreadCtrlBlock->Priority = 8; // 设置优先级为8
pThreadCtrlBlock->RemainderTicks = TICKS_OF_TIME_SLICE; // 设置时间片剩余大小
pThreadCtrlBlock->InitTicks = TICKS_OF_TIME_SLICE; // 设置初始化时间片大小
PspReadyThread(pThreadCtrlBlock);
}
PspThreadSchedule();//线程调度
KeEnableInterrupts(IntState); // 开中断
return;
}
具体操作验收时候看。
本文来自彬彬zhidao的博客,作者:彬彬zhidao,转载请注明原文链接:https://www.cnblogs.com/binbinzhidao/p/17884257.html