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实验实现的所有增改的源码文件,下载链接:下载