NachOS根据优先级的线程调度实验

:由于老师一直没发实验报告模板,所以直到今天才开始写,本次实验是延续上一次实验所做,并会对上一次实验中的某些过程进行修改(不理解的同学可以看一下上一篇博客)!!!

实验目的

(1)通过阅读相关源码,掌握NachOS调度的数据结构和实现过程;
(2)对NachOS线程描述进行完善,增加关于调度的内容;
(3)掌握NachOS线程调度的算法。

实验内容

(1)在NachOS线程描述中增加调度优先级的数据成员,并完善就绪队列管理的成员方法;
(2)实现基于优先级的FCFS调度。

实验过程

这次试验也是看了许多网上的方法,最后根据自己的能力写了一个自己能理解的方法,那就是:线程在进入后序队列时进行修改,让优先级高的线程排在优先级低的线程前面。

线程被调进就绪队列是在Yield函数(位置:thread.cc)中被调用:

kernel->scheduler->ReadyToRun(this); //将当前线程加入就绪队列

void
Thread::Yield ()
{
    Thread *nextThread;
    IntStatus oldLevel = kernel->interrupt->SetLevel(IntOff);  //关中断
    
    ASSERT(this == kernel->currentThread);  
    
    DEBUG(dbgThread, "Yielding thread: " << name);
    
    nextThread = kernel->scheduler->FindNextToRun();//找到下一个要运行的线程并使nextThrad指向它
    if (nextThread != NULL) {
	kernel->scheduler->ReadyToRun(this); //将当前线程加入就绪队列
	kernel->scheduler->Run(nextThread, FALSE); //运行下一个线程
    }
    (void) kernel->interrupt->SetLevel(oldLevel);
}

所以需要转到scheduler.cc中查看:scheduler->ReadyToRun

void
Scheduler::ReadyToRun (Thread *thread)
{
    ASSERT(kernel->interrupt->getLevel() == IntOff);
    DEBUG(dbgThread, "Putting thread on ready list: " << thread->getName());

    thread->setStatus(READY);       //将线程状态置为就绪态
    readyList->Append(thread);     //将线程加入就绪队列的队尾
}

所我们就可以参照 Append() (位置:list.cc)方法的结构,自己写一个根据优先级插入的方法,线程在进入后序队列时,让优先级高的线程排在优先级低的线程前面。对于 Append() 函数的结构大家就自己去查看就好,下面就直接阐述实现的过程。

实现过程:
(1)因为Append() 函数是list.cc中的函数,所以我们自己写插入函数的时候,也应该是在list.cc中实现,在这之前需要在list.h中声明一下:
void Newsort(T item,int priority);

template <class T>
class List {
  public:
    List();                     // initialize the list
    virtual ~List();            // de-allocate the list

    virtual void Prepend(T item);// Put item at the beginning of the list
    virtual void Append(T item); // Put item at the end of the list
//----------------------------------------------------------------------------------------修改处--调度实验
        void Newsort(T item,int priority);  //创建一个方法  对要传入就绪队列的线程排序插入(根据优先级的高低)

//----------------------------------------------------------------------------------------
    T Front() { return first->item; }

(2)然后就可以在list.cc中实现这个方法了(在这个函数中:priority值越大,则优先级越高):

//------------------------------------------------------------------修改处---实现list.h中的方法
template <class T>
void
List<T>::Newsort(T item, int priority)  // item-->当前线程  priority-->当前线程的优先级 
{

    ListElement<T> *element = new ListElement<T>(item);  //创建的一个以 item 为核心的节点 

    ASSERT(!IsInList(item));   //当要加入的线程已经在队列中时就打断运行,并输出报错信息

    if(IsEmpty())  //当队列为空时,就直接将当前的 item 放在里面 
    {
                Prepend(item);   // 将其放在最后面 ,因为是空的,所以最后最前都是一样的 
    }
    else
    {
         //当不为空时,则按照优先级值的大小进行插入 (类似于学数据结构时的链表的插入) 

        ListElement<T> *prev = first;   //定义一个指针指向头节点first 
        ListElement<T> *ptr = NULL;   //定义一个指针先放空, 用于后面插入的时候,记录前一个节点的位置 

        while(prev != NULL) //即当该节点不为空时,节点会不断向后移动,当移到最后一个节点的后面时就为空 
        {
            if(priority > prev->item->priority)   //当当前线程的优先级大于指针指向的节点的优先级时 
            {
                element->next = prev;   //当前线程的尾指针指向 prev 
                if(ptr == NULL)
                    first = element;//当当前队列中:当前线程的优先级是最高时,头节点直接指向它 
                else
                ptr->next = element;//如果ptr不为空,则ptr记录了上一个节点的位置,
                                         //则将上一个节点的尾指针指向当前线程             
                            numInList++; //队列中的元素个数加1         
                break;
            }
            ptr = prev;   //ptr记录当前指针的位置 
            prev = prev->next;  // prev向下移动一个节点 
        }
        if(ptr == last)   //如果ptr记录的已经为最后一个节点了,则说明当前线程的优先级是队列中最低的 
        {                                                                       //直接将它插入到最后面
                Append(item);                                                                                                                                
        }
    }
}
//-------------------------------------------------------------------------------------------

(3)方法已经实现了,那我们就可以使用这个方法了,方法应该要在哪里使用呢?之前的 Append() 函数是在scheduler->ReadyToRun(位置:scheduler.cc)中使用的,所以新创建的这个方法也在scheduler->ReadyToRun中使用就可以了,当然对其进行了一定的修改:

void
Scheduler::ReadyToRun (Thread *thread)
{
    ASSERT(kernel->interrupt->getLevel() == IntOff);
    DEBUG(dbgThread, "Putting thread on ready list: " << thread->getName());
    
    thread->setStatus(READY);       //将线程状态置为就绪态
//------------------------------------------------------------------------------------------ -修改处
//    readyList->Append(thread);     //将线程加入就绪队列的队尾-----------将原来的这句注释掉
    if(readyList->IsEmpty())    //如果就绪队列是空时
    {                              //  直接将当前线程加队尾(这里队尾和队头都是一样的,因为里面是空的)
        readyList->Append(thread); 
        return;               
    }
    readyList->Newsort(thread, thread->priority);   //不为空时调用自己写的函数,参数为:当前线程、优先级值

//------------------------------------------------------------------------------------------------  
}

:当我们这些条件都准备好后,就可以对thread.cc进行修改了。因为这次实验是在上一次实验基础做的,所以对于上一次实验的部分不在阐述,重新修改的地方将会用 //*************** 指明。

(4)首先对原构造函数Thread::Thread(char* threadName) (位置:thread.cc)
进行修改,将main线程和postal worker线程的优先级进行置为最高10000;将ping线程的优先级置为-1;将里面的输出语句注释掉,输出语句我们放到测试函数中去实现。

Thread::Thread(char* threadName)
{
//--------------------------------------------------------------------------------------修改处
        if(++threadNUM>=MAX_SIZE){                    //当线程数大于规定的最大线程数时,输出最大线程数,并打断程序执行
        cout<<"最大线程数:"<<MAX_SIZE<<endl;
        ASSERT(threadNUM<=MAX_SIZE);
        }

        int i=0;
        for(i=0;i<MAX_SIZE;i++){    //每次创建一个新的线程,这里便要从0开始检查,直到到达未被使用的线程号为止
                if(Tstatus[i]==0){    //如果线程号未被使用,便执行以下操作
                this->tid=i+1;    //  给线程赋上一个id号, 因为是从0开始计算,所id号需要加1

                srand(time(0)+i);    //设置一个随机种子         
                this->priority=rand()%(100);  //给每个线程附加一个随机的优先级值
                Tstatus[i]=1;       //将该线程号状态置为1,表示已经被使用了
                break;
                }
        }
//************************************************************************************************修改处
        if(strcmp(threadName,"main")==0||strcmp(threadName,"postal worker")==0)
        this->priority =10000;
        if(strcmp(threadName,"ping")==0)
        this->priority=-1;
//***********************************************************************************************
    name = threadName;
    stackTop = NULL;
    stack = NULL;
    status = JUST_CREATED;
    for (int i = 0; i < MachineStateSize; i++) {
        machineState[i] = NULL;         // not strictly necessary, since
                                        // new thread ignores contents 
                                        // of machine registers
    }
    space = NULL;
//*****************************************************************************************************修改处---将上一次实验的修改处注释掉
//--------------------------------------------------------------------------修改处
//      cout<<"线程名:"<<this->getName()<<"    线程id:"<<this->getid()<<"    线程优先级:"<<this->priority<<endl;
        //输出线程一系列参数:名字、id、优先级。
//-------------------------------------------------------------------------------
}

(5)对析构函数Thread::~Thread()(位置:thread.cc)进行修改:

Thread::~Thread()
{
    DEBUG(dbgThread, "Deleting thread: " << name);
//---------------------------------------------------------------------------修改处
        Tstatus[tid-1]=0;     //将该线程号的状态置0,即被释放了之后,该线程号可以被其他线程使用
        threadNUM--;       //  将现在线程的总数减1

//-----------------------------------------------------------------------------
    ASSERT(this != kernel->currentThread);
    if (stack != NULL)
        DeallocBoundedArray((char *) stack, StackSize * sizeof(int));
}

(6)对测试函数Thread::SelfTest()(位置:thread.cc)进行修改:

void
Thread::SelfTest()
{
    DEBUG(dbgThread, "Entering Thread::SelfTest");
//-----------------------------------------------------------------------修改处--以下部分为原代码被注释掉
/*    Thread *t = new Thread("forked thread");

    t->Fork((VoidFunctionPtr) SimpleThread, (void *) 1);
    kernel->currentThread->Yield();
    SimpleThread(0);
*/
//************************************************************修改处---将第一次修改的部分注释掉
//------------------------------------------------------------------修改处-

/*      Thread *t[124];   //定义一个大小为124的线程数组,因为在程序运行中会产生另外4个线程,1个main线程
                                                     // 1个postal worker 线程,2个ping线程,所以这里创建124个线程正好满足128个线程
        for(int i = 0;i < 124;i++){
                t[i] = new Thread("线程");
                }       */
//------------------------------------------------------------------------
//******************************************************************修改处---添加本次的修改处
        Thread *t[5];
        for(int i=0;i<5;i++){
                t[i]= new Thread("New Thread");
                cout<<"线程名: "<< t[i]->getName()<<"   线程ID: "<< t[i]->getid()<<"   优先级: "<<t[i]->getPriority()<<endl;
                if(i==4) cout<<endl;
                t[i]->Fork((VoidFunctionPtr)SimpleThread,(void *)t[i]);
        }
}

(7)注:我们在修改的测试函数中SimpleThread,(void *)t[i]), 参数变为了 t[i] ,所以下面我们修改SimpleThread (int which) (位置:thread.cc)函数时需要对其参数一并修改:

static void
SimpleThread(Thread *t)     //将这里的参数类型变为 Thread类型
{
    int num;
    for (num = 0; num < 2; num++) {  //这里就只运行2次便于观察
        cout<<"线程名: "<<t->getName()<<"   线程ID:"<<t->getid()<<"   优先级: "<<t->getPriority()<<"   lopped: "<<num<<"   times"<<endl;
        kernel->currentThread->Yield();
    }
}

(8)改到这里就以基本完成了,但你这个时候去运行就会发现会报错,这是因为priority是一个私有变量,无法传递到某些类中去使用,所以就必须将它变为公开的变量,这就需要到 thread.h 中进行修改(这里将 tid 一并转移到了公开区域),同时创建了一个getPriority() 函数:

class Thread {
  private:
    // NOTE: DO NOT CHANGE the order of these first two members.
    // THEY MUST be in this position for SWITCH to work.
    int *stackTop;                       // the current stack pointer
    void *machineState[MachineStateSize];  // all registers except for stackTop
//*******************************************************************将私有变量 priority和 tid 注释掉
//------------------------------------------------------------------------------修改处
//      int tid;   //定义每一个线程的id号
//      int priority;    //定义每个线程的优先级
        //因为每个线程的id和由优先级都是独有的,所以定义在私有变量
//--------------------------------------------------------------------------------


  public:
    Thread(char* debugName);            // initialize a Thread 
    ~Thread();                          // deallocate a Thread
                                        // NOTE -- thread being deleted
                                        // must not be running when delete 
                                        // is called

    // basic thread operations
//*************************************************修改处---将上面两个变量定义到公开区域,同再创建一个getPriority() 函数
//---------------------------------------------------------------------------修改处
        int tid;   //定义每一个线程的id号
        int priority;    //定义每个线程的优先级
        int  getid()
                {return this->tid;}     //返回该线程的id号
        int getPriority()
                { return this->priority;}       //返回新构造函数的优先级值
//--------------------------------------------------------------------------

这样就完成了所有的修改,下面是实验运行的结果:
在这里插入图片描述

注1:由于使用的是原构造函数,所以就会有main线程和postal worker线程的出现,但是我们将原构造函数中的输出语句放到了测试函数中,所以就不会输出这两个线程的一系列参数,但是它们是存在的,所以最后输出的ID号是从3开始的,但不影响结果,写好注释说明即可。

注2:实验环境:Ubuntu14.04。

写的不是很好,大家就将就看一下,有错误的地方大家多多包涵。
最后希望大家早日完成实验报告,少熬夜。

  • 17
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值