0.写在前面
其实昨天就把这篇写完了,可是遇到了一些突发事件,暂时还没想好自己的出路在哪,争取这两天把课程设计的实验全都写完吧。。。。。我知道大家现在都很难过,生活上,学业上,事业上。。。。但是还是希望大家能一起加油度过这段日子
1.关于实验的说明
首先这个实验的最基本要求,就是修改一下nachos的线程调度策略,让其变为具有优先级的县城调度。换言之,就是加上优先级属性,然后在调度(对于这个实验来说,调度可以具体化一些,就是线程从就绪态转化为运行态,然后进入队列或者cpu的过程中)。
因为实验本身不太难(落实到代码上,只需要改动两三个地方就可以了,加起来不到10行代码),而且其实我也不会c,源码的解释在这篇可能不是很地道和详细,但我还是尽力去完成这些东西,还是那句话,如有不足,还望指出,感激不尽
2.关于一些模块以及函数的解析
线程相关函数的分析
//构造函数
Thread::Thread(const char* threadName,int threadPriority=0)
//系够函数
Thread::~Thread()
//创建新的线程
void
Thread::Fork(VoidFunctionPtr func, _int arg)
//检查线程是否超出内存
void
Thread::CheckOverflow()
//结束线程(转化为结束态)
void
Thread::Finish ()
//转化为就绪态
void
Thread::Yield ()
//转化为阻塞态
void
Thread::Sleep ()
//为线程分配空间
void
Thread::StackAllocate (VoidFunctionPtr func, _int arg)
//不知道干啥的,应该没啥用
void
Thread::SaveUserState()
void
Thread::RestoreUserState()
至于schedule下面只需要知道这一个函数就可以了,这个函数使用来切换线程的
void
Scheduler::ReadyToRun (Thread *thread)
{
DEBUG('t', "Putting thread %s on ready list.\n", thread->getName());
thread->setStatus(READY);
// readyList->Append((void *)thread);
readyList->SortedInsert((void *)thread,thread->getPriority());
}
3.关于实验需要做什么
好的吧,那么我们直接开始解析内容了。
首先实现说明,我们需要修改的模块只有两个,schedule系列(用来管理调度),thread(用来管理线程实体本身)就可以将正常的非抢夺式线程调度,转化为一个优先级调度。最后在threadTest下面fork几个新的线程。
而具体的实现思路其实就两点,剩下的都是nachos本身完成了实现:
1. 线程本身,需要注明优先级
2. 而优先级调度的实现,在这里我们是通过在运行态转化为就绪态,也就是进入就绪队列的时候,实现这样一个”按优先级顺序插入“,而不是直接插入尾部。
好吧,我知道这样解释其实挺苍白的,所以我们在这里直接解释一下如何进行操作
首先是对于线程本身这个类:
3.0:准备工作
需要将如下几个文件从/threads中复制过来
3.1:对于thread.cc和thread.h的修改
对于头文件来说,我们需要做的是优先级,在这里用一个整数来进行表示
然后,增加一个获取优先级的方法,让整个系统封装严密一些
在然后,修改thread的构造方法声明,添加优先级参数:
这样,在h文件中得到的一个类,(改动的部分已经在下面了)
class Thread {
public:
Thread(const char* debugName , int priority=0); // initialize a Thread
int getPriority(){ return priority;} //返回优先级的方法
private:
int priority;
然后是更改一下thread.cc文件中,关于构造方法的具体实现
(注意构造方法中,优先级属性请设置为默认参数,因为有些本实验涉及不到的东西会遇到一些问题,因此需要保留原本的构造方法的有效性)
3.2:对于scheduler.cc以及scheduler.h的修改
优先级的实现,在thread上已经完成了,这个不用多说
接下来是如何根据优先级实现调用,其实实现原理就是从”先进先出“的尾部插入,到”优先级插入“,而这个函数,nachos已经帮我们实现了
直接修改其中的readytorun函数即可
void
Scheduler::ReadyToRun (Thread *thread)
{
DEBUG('t', "Putting thread %s on ready list.\n", thread->getName());
thread->setStatus(READY);
// readyList->Append((void *)thread); 将这个修改为别的
readyList->SortedInsert((void *)thread,thread->getPriority());
}
3.3:threadtest.cc文件
这个文件主要是用来进程的改动而已,在函数中新增几个两个线程即可
//----------------------------------------------------------------------
// ThreadTest
// Set up a ping-pong between two threads, by forking a thread
// to call SimpleThread, and then calling SimpleThread ourselves.
//----------------------------------------------------------------------
void
ThreadTest()
{
DEBUG('t', "Entering SimpleTest");
Thread *t1 = new Thread("forked thread1");
Thread *t2 = new Thread("forked thread2");
Thread *t3 = new Thread("forked thread3");
t1->Fork(SimpleThread, 1); //第二个参数是序号
t2->Fork(SimpleThread, 2);
t3->Fork(SimpleThread, 3);
SimpleThread(0);
}
最后按照我们实验1的方式,使用make指令编译出可执行文件即可执行了。
4.实验内容(正文报告)
1. 分析说明原有的Nachos调度流程
原有的nachos调度流程,虽然已经实现了”优先级属性”在listElement中,但是本质上在插入的时候,对每个线程封装的优先级均为“0”,也就是先进显出的调度策略。
2. 设计并且实现具有静态优先级的非抢占式线程调度策略。
修改内容:
(1)首先引入需要的模块
需要将如下几个文件从/threads中复制过来
(2)然后对应模块进行修改
对于头文件来说,我们需要做的是优先级,在这里用一个整数来进行表示
然后,增加一个获取优先级的方法,让整个系统封装严密一些
在然后,修改thread的构造方法声明,添加优先级参数:
这样,在h文件中得到的一个类,(改动的部分已经在下面了)
class Thread {
public:
Thread(const char* debugName , int priority=0); // initialize a Thread
int getPriority(){ return priority;} //返回优先级的方法
private:
int priority;
然后是更改一下thread.cc文件中,关于构造方法的具体实现
优先级的实现,在thread上已经完成了
接下来是如何根据优先级实现调用,其实实现原理就是从”先进先出“的尾部插入,到”优先级插入“,而这个函数,nachos已经实现了
直接修改其中的readytorun函数即可
void
Scheduler::ReadyToRun (Thread *thread)
{
DEBUG('t', "Putting thread %s on ready list.\n", thread->getName());
thread->setStatus(READY);
// readyList->Append((void *)thread); 将这个修改为别的
readyList->SortedInsert((void *)thread,thread->getPriority());
}
这个文件主要是用来进程的改动而已,在函数中新增几个两个线程即可
//----------------------------------------------------------------------
// ThreadTest
// Set up a ping-pong between two threads, by forking a thread
// to call SimpleThread, and then calling SimpleThread ourselves.
//----------------------------------------------------------------------
void
ThreadTest()
{
DEBUG('t', "Entering SimpleTest");
Thread *t1 = new Thread("forked thread1");
Thread *t2 = new Thread("forked thread2");
Thread *t3 = new Thread("forked thread3");
t1->Fork(SimpleThread, 1); //第二个参数是序号
t2->Fork(SimpleThread, 2);
t3->Fork(SimpleThread, 3);
SimpleThread(0);
}
最后按照我们实验1的方式,使用make指令编译出可执行文件,并且运行这个可执行文件,得到结果如下
3. 上下文切换次数变化
首先节选一部分,指令nachos -t d指令打印出来的信息
经过对比发现,和simpleThread的条目数目对不上。
实际上这个结果的原因是由于上下文的频繁切换,因为simpleThread中的执行逻辑是简单的线程放弃cpu以及切换,也可能是频繁的上下文切换导致了错误。
通过如下的修改方式可以实现一定程度的缓解,通过缓冲区统一打印,减少一定程度上的上下文切换频率。
void
SimpleThread(_int which) //
{
int num;
int p=0
char buffer[100];
for (num = 0; num < 5; num++) { //减少上下文切换次数差不多就是在这里
p+=snprintf(buffer+printCount,........)//累加到缓冲区中
}
printf('%s',buffer)
currentThread->Yield()
}
4. 优先级调度老化的实现
线程调度老化指的是随着时间的推移,线程优先级降低或者失去掉去机会的情况。
对于nachos来说,增加老化机制的方法可以为在线程切换并且转化为就绪态,在插入就绪队列之前,可以适当对优先级进行自减操作。
首先需要对thread进行进一步的修改,增加setPriority来修改线程的优先级
然后在Yield方法中,在线程对象重新被放入readyList之前,将其优先级属性降低即可。
这样即可实现老化机制