Nachos实验实现线程id、限制线程数和更改调度算法(按优先级调度)

Nachos实验1实现线程id、限制线程数和更改调度算法(按优先级调度)

一、理解Nachos线程的运行及调度原理

Nachos实验主要是为了使我们深入的理解操作系统结构,Nachos也是一个操作系统,但是它很简便,我们可以很容易的对它进行改动;通过对Nachos的实验来增加我们对操作系统的理解,当前这个实验主要是通过我们实现Nachos线程id和优先级调度来对操作系统进程和线程的理解。Nachos里面主要分为内核线程(例如main线程)和用户线程,主要通过主线程来操作,例如新建和调度线程。其实对于增加线程id和限制最大线程数,只要了解进程的实现原理,看代码也会非常的容易理解,这样就会非常简单了。
对于理解这次实验代码,我认为,需要看thread.h、thread.cc、scheduler.h、scheduler.cc、List.h、List.cc、main.h、main.cc,对于main.h和main.cc,只需要知道在运行Nachos的时候加-K命令(“./nachos -K”)就会进入线程测试就行了。

二、增加线程ID和最大线程数

在增加实现线程id和最大线程数时,thread类的构造函数尽量不要改动,只需再重构一个构造方法,因为之前的构造函数可能在Nachos的其他类里会调用它,所以我们不能再之前的构造函数里构造,这样才不会打乱原系统的正确性,所以需要增加一个构造函数Thread(char* debugName,int priority);至于int priority先不用管,这是之后优先级调度需要用到的参数,不影响我们使用这个构造函数

1.增改class thread,增加线程id成员和一些需要用到的成员方法

#define MAX_SIZE  128//最大线程数
//pk数组主要记录线程号被使用的状态
//下标位线程号,数组对应值位线程号状态
//对应值为0时,表示该线程号没有线程占用
//对应值为1时,表示该线程号被占用
static int pk[MAX_SIZE]={0};
static int threadMAX = 0;//记录线程数

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

	int tid;
  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
	Thread(char* debugName,int priority);//新构造函数
	int getTid(){return this->tid;};//获得线程id

    void Fork(VoidFunctionPtr func, void *arg); //将线程加入就绪队列
    				// Make thread run (*func)(arg)
    void Yield();  //打断当前线程,运行就绪队列里的线程
					// Relinquish the CPU if any 
				// other thread is runnable
    void Sleep(bool finishing);//将当前线程阻塞
								 // Put the thread to sleep and 
				// relinquish the processor
    void Begin();	// Startup code for the thread	
    void Finish();  //线程运行结束	
						// The thread is done executing
    
    void CheckOverflow();   	// Check if thread stack has overflowed
    void setStatus(ThreadStatus st) { status = st; }//设置线程状态
    char* getName() { return (name); }//获取线程名字
    void Print() { cout << name; }//打印线程名字
    void SelfTest();	//测试方法	// test whether thread impl is working

  private:
    // some of the private data for this class is listed above
    
    int *stack; 	 	// Bottom of the stack 
				// NULL if this is the main thread
				// (If NULL, don't deallocate stack)
    ThreadStatus status;	// ready, running or blocked
    char* name;

    void StackAllocate(VoidFunctionPtr func, void *arg);
    				// Allocate a stack for thread.
				// Used internally by Fork()

// A thread running a user program actually has *two* sets of CPU registers -- 
// one for its state while executing user code, one for its state 
// while executing kernel code.

    int userRegisters[NumTotalRegs];	// user-level CPU register state

  public:
    void SaveUserState();		// save user-level register state
    void RestoreUserState();		// restore user-level register state

    AddrSpace *space;			// User code this thread is running.
};

2.在thread.cc里面增加我们需要的功能,切记,我们是新增构造函数,不是在原构造函数更改!!

Thread::Thread(char* threadName)//原构造函数,不要更改它
{
    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;
}
Thread::Thread(char* threadName,int priority)//新构造函数
{

	if(++threadMAX > MAX_SIZE){//限制线程数
		cout<<"最大线程数:"<<MAX_SIZE<<"!!!"<<endl;
		ASSERT(threadMAX<=MAX_SIZE);
	}
	int j;
	for(j=0;j<MAX_SIZE;j++){//设置id
		if(pk[j]==0){
			this->tid = j+1;
			pk[j] = 1;
			break;
		}
	}

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

3.增改析构函数

Thread::~Thread()
{
    DEBUG(dbgThread, "Deleting thread: " << name);
	pk[tid -1] = 0;//线程id撤销
	threadMAX--;//线程数减一
    ASSERT(this != kernel->currentThread);
    if (stack != NULL)
	DeallocBoundedArray((char *) stack, StackSize * sizeof(int));
}

4.在测试函数增改测试代码

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[130];
	for(int i = 0;i < 130;i++){
		t[i] = new Thread("线程",1);
		cout<<t[i]->getName()<<t[i]->getTid()<<endl;
	}
}

测试

make
./nachos -K

编译
测试

结果


在这里插入图片描述
实现线程id和限制最大数的所有增改源码thread.h和thread.cc,下载链接:下载

三、实现更改Nachos线程调度,按优先级调度

对于按优先级调度,我的想法是,因为创建一个线程之后,需要加入就绪队列就能运行,Fork方法就实现了将线程(readyList)加入就绪队列里的功能,还有Yield方法的功能是打断当前正在运行的线程,运行就绪队列里的线程,再将被打断的线程加入就绪队列。而加入就绪队列是调用的是Schedule类里的ReadyToRun(Thread *thread)方法,ReadyToRun方法实现了将线程加入在就绪队列里的队尾,就是一个先进先调度的调度机制。我经历了无数次错误之后,最后把思想集中在了加入就绪队列时按照线程优先级加入队列。实现如下(这是接着之前线程id做的):
1.增改thread.h文件,增加优先级成员

#define MAX_SIZE  128  //最大线程数
//pk数组主要记录线程号被使用的状态
//下标位线程号,数组对应值位线程号状态
//对应值为0时,表示该线程号没有线程占用
//对应值为1时,表示该线程号被占用
static int pk[MAX_SIZE]={0};
static int threadMAX = 0;//记录线程数

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

	int tid;//线程id
	int priority;//线程优先级,值越大优先级越高
  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
	Thread(char* debugName,int priority);//新构造函数
	int getTid(){return this->tid;};//获得线程id
	int getPriority(){return priority;};//获得线程优先级


    void Fork(VoidFunctionPtr func, void *arg); //将线程加入就绪队列
    				// Make thread run (*func)(arg)
    void Yield();  //打断当前线程,运行就绪队列里的线程
					// Relinquish the CPU if any 
				// other thread is runnable
    void Sleep(bool finishing);//将当前线程阻塞
								 // Put the thread to sleep and 
				// relinquish the processor
    void Begin();	// Startup code for the thread	
    void Finish();  //线程运行结束	
						// The thread is done executing
    
    void CheckOverflow();   	// Check if thread stack has overflowed
    void setStatus(ThreadStatus st) { status = st; }//设置线程状态
    char* getName() { return (name); }//获取线程名字
    void Print() { cout << name; }//打印线程名字
    void SelfTest();	//测试方法	// test whether thread impl is working

  private:
    // some of the private data for this class is listed above
    
    int *stack; 	 	// Bottom of the stack 
				// NULL if this is the main thread
				// (If NULL, don't deallocate stack)
    ThreadStatus status;	// ready, running or blocked
    char* name;

    void StackAllocate(VoidFunctionPtr func, void *arg);
    				// Allocate a stack for thread.
				// Used internally by Fork()

// A thread running a user program actually has *two* sets of CPU registers -- 
// one for its state while executing user code, one for its state 
// while executing kernel code.

    int userRegisters[NumTotalRegs];	// user-level CPU register state

  public:
    void SaveUserState();		// save user-level register state
    void RestoreUserState();		// restore user-level register state

    AddrSpace *space;			// User code this thread is running.
};

2.增改thread.cc文件

Thread::Thread(char* threadName)//内核线程创建
{   int j;
    for(j=0;j<2;j++){//设置id
        if(pk[j]==0){
            this->tid = j-1;
            pk[j] = 1;
            break;
        }
    }

    priority = 100000;
    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;
}

Thread::Thread(char* threadName,int priority)
{

	if(++threadMAX > MAX_SIZE){//限制线程数
		cout<<"最大线程数:"<<MAX_SIZE<<"!!!"<<endl;
		ASSERT(threadMAX<=MAX_SIZE);
	}
	int j;
	for(j=2;j<MAX_SIZE;j++){//设置id
		if(pk[j]==0){
			this->tid = j-1;
			pk[j] = 1;
			break;
		}
	}
	this->priority = priority;
    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;
}

Thread::~Thread()
{
    DEBUG(dbgThread, "Deleting thread: " << name);
    pk[tid + 1] = 0;
    threadMAX--;
    ASSERT(this != kernel->currentThread);
    if (stack != NULL)
    DeallocBoundedArray((char *) stack, StackSize * sizeof(int));
}

3.增改List.h文件,增加成员方法


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

    T Front() { return first->item; }
    				// Return first item on list
				// without removing it
    T RemoveFront(); 		// Take item off the front of the list
    void Remove(T item); 	// Remove specific item from list

    bool IsInList(T item) const;// is the item in the list?

    unsigned int NumInList() { return numInList;};
    				// how many items in the list?
    bool IsEmpty() { return (numInList == 0); };
    				// is the list empty? 

    void Apply(void (*f)(T)) const; 
    				// apply function to all elements in list

    virtual void SanityCheck() const;	
				// has this list been corrupted?
    void SelfTest(T *p, int numEntries);
				// verify module is working

	void setNumInList(int numInList){this->numInList = numInList;}//设置队列元素个数
	ListElement<T>* getfirst(){return first;};//get头指针
	ListElement<T>* getlast(){return last;};//get尾指针
	void setfirst(ListElement<T> *first){this->first = first;};//设置头结点
	void setlast(ListElement<T>* last){this->last = last;};//设置尾结点
  protected:
    ListElement<T> *first;  	// Head of the list, NULL if list is empty
    ListElement<T> *last;	// Last element of list
    int numInList;		// number of elements in list

    friend class ListIterator<T>;
};

4.增改schedule.h文件,增加成员方法


class Scheduler {
  public:
    Scheduler();		// Initialize list of ready threads 
    ~Scheduler();		// De-allocate ready list

    void ReadyToRun(Thread* thread);	
    				// Thread can be dispatched.
    Thread* FindNextToRun();	// Dequeue first thread on the ready 
				// list, if any, and return thread.
    void Run(Thread* nextThread, bool finishing);
    				// Cause nextThread to start running
    void CheckToBeDestroyed();// Check if thread that had been
    				// running needs to be deleted
    void Print();		// Print contents of ready list
    
    // SelfTest for scheduler is implemented in class Thread
	
	void ReadyToRunPriority(Thread* thread); //将线程按优先级加入就绪队列    
  private:
    List<Thread *> *readyList;  // queue of threads that are ready to run,
				// but not running
    Thread *toBeDestroyed;	// finishing thread to be destroyed
    				// by the next thread that runs
};

5.增改schedule.cc文件(实现ReadyToRunPriority方法)

void Scheduler::ReadyToRunPriority(Thread *thread){
	//将线程thread按优先级加入就绪队列
	ASSERT(kernel->interrupt->getLevel() == IntOff);
	DEBUG(dbgThread, "Putting thread on ready list: " << thread->getName());
	thread->setStatus(READY);

	ASSERT(!readyList->IsInList(thread));//断言
	ListElement<Thread *> *element = new ListElement<Thread *>(thread);//创建新结点
	if(readyList->IsEmpty()){
		//当就绪队列为空时,直接加入队列
		readyList->setfirst(element);
		readyList->setlast(element);
		readyList->setNumInList(readyList->NumInList()+1);
	}else{
		//遍历就绪队列
		//当线程优先级大于就绪队列结点所对应线程优先级时
		//将element插入在所对应结点之前
		if(thread->getPriority() > readyList->getfirst()->item->getPriority()){
			element->next = readyList->getfirst();
			readyList->setfirst(element);
			readyList->setNumInList(readyList->NumInList()+1); 
		}else{
			ListElement<Thread *> *pre = readyList->getfirst(); 
			ListElement<Thread *> *ptr = pre->next;
			while(ptr != NULL){
				if(thread->getPriority() > ptr->item->getPriority()){
					element->next = ptr;
					pre->next = element;
					readyList->setNumInList(readyList->NumInList()+1); 
					break;
				}else{
					pre = ptr;
					ptr = ptr->next;
				}
			}
			if(ptr == NULL){
				element->next = NULL;
				pre->next = element;
				readyList->setlast(element);
				readyList->setNumInList(readyList->NumInList()+1); 
			}
		}
	}
}

6.在thread.cc里更改Fork方法

void
Thread::Fork(VoidFunctionPtr func, void *arg)
{
    Interrupt *interrupt = kernel->interrupt;
    Scheduler *scheduler = kernel->scheduler;
    IntStatus oldLevel;

    DEBUG(dbgThread, "Forking thread: " << name << " f(a): " << (int) func << " " << arg);

    StackAllocate(func, arg);

    oldLevel = interrupt->SetLevel(IntOff);
    scheduler->ReadyToRunPriority(this);//将线程按优先级加入就绪队列
                                    // ReadyToRun assumes that interrupts 
                    // are disabled!
    (void) interrupt->SetLevel(oldLevel);
}

7.在thread.cc里增改Yield方法

void
Thread::Yield ()
{
    Thread *nextThread;
    IntStatus oldLevel = kernel->interrupt->SetLevel(IntOff);

    ASSERT(this == kernel->currentThread);

    DEBUG(dbgThread, "Yielding thread: " << name);

    /*nextThread = kernel->scheduler->FindNextToRun();
    if (nextThread != NULL) {
    kernel->scheduler->ReadyToRun(this);
    kernel->scheduler->Run(nextThread, FALSE);
    }*/
    kernel->scheduler->ReadyToRunPriority(this);//将当前线程按优先级加入就绪队列
    nextThread = kernel->scheduler->FindNextToRun();
    if (nextThread != NULL) {
    kernel->scheduler->Run(nextThread, FALSE);
    }
    (void) kernel->interrupt->SetLevel(oldLevel);
}


7.在thread.cc里增改SimpleThread方法(可改可不改)

static void
SimpleThread(int which)
{
    int num;

    for (num = 0; num < 4; num++) {

    cout << "*** thread " << which<< " looped " << num << " times\n";
        kernel->currentThread->Yield();
    }
    //kernel->currentThread->Yield();

}


8.在thread.cc里增改测试方法
下面会用到C语言函数库的srand()和time()函数,需要增加头文件#include “time.h"和#include"stdlib.h”,记得加上去,不然会报错

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[130];
    for(int i = 0;i < 130;i++){
        t[i] = new Thread("线程",1);
        cout<<t[i]->getName()<<t[i]->getTid()<<endl;
    }*/

     Thread *t[4];
    for(int i = 0;i < 4;i++){
        srand(time(NULL)+i);
        t[i] = new Thread("线程",rand()%10+1);
        cout<<t[i]->getName()<<threadMAX<<" tid:"<<t[i]->getTid()<<" priority:"<<t[i]->getPriority()<<endl;
        t[i]->Fork((VoidFunctionPtr) SimpleThread, (void *)threadMAX);
    }



}

测试
在这里插入图片描述

本次Nachos实验实现的所有增改的源码文件,下载链接:下载

  • 26
    点赞
  • 96
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值