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
#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 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.增改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->ReadyToRunPriority(this);//將執行緒按優先順序加入就緒佇列
kernel->scheduler->Run(nextThread, FALSE);
}
(void) kernel->interrupt->SetLevel(oldLevel);
}
7.在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[130];
for(int i = 0;i < 130;i++){
t[i] = new Thread("執行緒",1);
cout<<t[i]->getName()<<t[i]->getTid()<<endl;
}*/
Thread *t = new Thread("執行緒1",1);
cout<<t->getName()<<"優先順序:"<<t->getPriority()<<endl;
t->Fork((VoidFunctionPtr) SimpleThread, (void *)t->getTid());
Thread *t1 = new Thread("執行緒2",4);
cout<<t1->getName()<<"優先順序:"<<t1->getPriority()<<endl;
t1->Fork((VoidFunctionPtr) SimpleThread, (void *)t1->getTid());
Thread *t2 = new Thread("執行緒3",3);
cout<<t2->getName()<<"優先順序:"<<t2->getPriority()<<endl;
t2->Fork((VoidFunctionPtr) SimpleThread, (void *)t2->getTid());
}
測試
本次Nachos實驗實現的所有增改原始碼的所有原始碼,下載連結:
相關文章
- 多執行緒------執行緒與程式/執行緒排程/建立執行緒執行緒
- Java執行緒的排程Java執行緒
- 多優先順序執行緒池實踐執行緒
- Java 多執行緒基礎(十一)執行緒優先順序和守護執行緒Java執行緒
- 程序中的執行緒排程執行緒
- join、volatile、newSingleThreadLatch 實現執行緒順序執行thread執行緒
- Java之執行緒的優先順序Java執行緒
- 【java】【多執行緒】睡眠/守護/加入/禮讓執行緒,執行緒優先順序(4)Java執行緒
- libgo原始碼分析之多執行緒協程管理和排程Go原始碼執行緒
- 利用訊號量實現執行緒順序執行執行緒
- Python執行緒專題10:queue、多執行緒按順序執行Python執行緒
- Linux排程器:程序優先順序Linux
- Java排程執行緒池ScheduledThreadPoolExecutor原始碼分析Java執行緒thread原始碼
- 2.2.5排程演算法:時間片輪轉、優先順序排程、多級反饋排程演算法
- 多執行緒,執行緒類三種方式,執行緒排程,執行緒同步,死鎖,執行緒間的通訊,阻塞佇列,wait和sleep區別?執行緒佇列AI
- Golang原始碼學習:排程邏輯(三)工作執行緒的執行流程與排程迴圈Golang原始碼執行緒
- 多執行緒實現順序迴圈列印執行緒
- RxJava原始碼解析(二)—執行緒排程器SchedulerRxJava原始碼執行緒
- Python3 執行緒優先順序佇列( Queue)Python執行緒佇列
- 從Java到JVM到OS執行緒的優先順序JavaJVM執行緒
- 執行緒池建立執行緒的過程執行緒
- C# 併發控制框架:單執行緒環境下實現每秒百萬級排程C#框架執行緒
- jdk排程任務執行緒池ScheduledThreadPoolExecutor工作原理解析JDK執行緒thread
- 程序 執行緒 協程執行緒
- Java中如何保證執行緒順序執行Java執行緒
- 重走JAVA之路(六):你應該要知道的執行緒排程Java執行緒
- linux中設定程式排程的優先順序別Linux
- SpringBoot執行緒池和Java執行緒池的實現原理Spring Boot執行緒Java
- 如何使用Rust的gaffer實現優先順序的微批處理排程器 - njkRust
- .net使用Task多執行緒執行任務 .net限制執行緒數量執行緒
- 模擬主執行緒等待子執行緒的過程執行緒
- 程式、執行緒和協程的概念執行緒
- Loom會造成CPU密集型執行緒的不公平排程OOM執行緒
- kafka多執行緒順序消費Kafka執行緒
- 【騏程】多執行緒(上)執行緒
- 協程、執行緒與程式執行緒
- 執行緒和執行緒池執行緒
- 【高併發】深入理解執行緒的執行順序執行緒