[山东大学操作系统课程设计]实验2

本文讲述了如何在Nacho操作系统中实现优先级线程调度,包括修改thread和scheduler模块,引入优先级属性,调整调度函数以按优先级插入就绪队列,以及上下文切换优化和线程调度老化机制的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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之前,将其优先级属性降低即可。

这样即可实现老化机制

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值