09_实验八_拓展实验三

拓展实验三:线程调度算法改进

实验目的

实现多级反馈队列调度算法

实验步骤

  1. 实现时间片轮转调度算法。

  2. 修改时间片的大小 TICKS_OF_TIME_SLICE 为 100,方便观察执行后的效果。

  3. 在控制台命令“rr”的处理函数中,将 Sleep 时间更改为 200*1000,这样可以有充足的时间查看优先级降低后的效果。

  4. 修改线程控制块(TCB)结构体,在其中新增两个成员,一个是线程整个生命周期中合计使用的时间片数量,另一个是线程的初始时间片数量。

  5. 修改“rr”命令在控制台输出的内容和格式,不再显示线程计数,而是显示线程初始化时间片的大小,已使用时间片的合计数量,剩余时间片的数量。注意,在调用fprintf 函数格式化字符时,需要在字符串的末尾增加一个空格,否则会导致输出异常。

  6. 在实现多级反馈队列调度算法后(注意:数字越大,优先级越高,反之,数字越小,优先级越低),使用实验 6 中提供的“rr”命令,查看各个线程的优先级逐步降低的过程。

  7. 由于 EOS 没有提供鼠标,可以使用键盘事件或者控制台命令使线程优先级提升。由于键盘事件与线程之间没有建立一个明确的会话关系,所以还需要解决使用键盘事件提升哪个线程优先级的问题。一个简单的方式是,在键盘的中断处理程序中(在io/driver/keyboard.c 文件的 396 行的 KbdIsr 函数),如果当前线程(注意不能是 2 号线程)处于运行状态并且优先级大于 0 小于 8 的话(由于空闲线程的优先级为 0,不能更改该线程的优先级,如果当前线程的优先级为 8,没有必要再做提升线程优先级的操作),按下空格键,响应键盘事件后,就将其优先级提升为默认的优先

  8. 级即可。关于键盘中断相关的内容可以参考实验 12。

  9. 使用控制台命令提升线程优先级,在 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;
}

image-20231208081436994

修改时间片的大小 TICKS_OF_TIME_SLICE 为 100,方便观察执行后的效果。

#define TICKS_OF_TIME_SLICE		100

将 ps/psp.h 的 TICK_OF_TIME_SLICE 设置为 100,方便观察执行后的效果。

效果:

image-20231208081812047

在控制台命令“rr”的处理函数中,将 Sleep 时间更改为 200*1000,这样可以有充足的时间查看优先级降低后的效果。

// 当前线程等待一段时间。由于当前线程优先级 24 高于新建线程的优先级 8,
	// 所以只有在当前线程进入“阻塞”状态后,新建的线程才能执行。
	Sleep(200 * 1000);

image-20231208082310871

修改线程控制块(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;
	
}

效果:

image-20231208083208478

使用键盘事件或者控制台命令使线程优先级提升

在 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;
}

image-20231208085249166

具体操作验收时候看。

posted @ 2023-12-07 23:27  彬彬zhidao  阅读(786)  评论(7编辑  收藏  举报