Nachos Project 2 Lecturer: Hao-Hua Chu TA: Chun-Po Wang (Artoo) Date: 2008/10/14 Material Provided by Yuan-Hao Chang, Yung-Feng Lu Project 2 • Implement Thread Scheduling – Default is FCFS – Priority scheduling + Round Robin 2 Summary • Motivation & Objective • Nachos – Thread – Scheduler – Timesharing (Round-robin) • Requirement • Submission 3 Motivation & Objective • Modern operating systems should have the ability to schedule multiple threads. – Low waiting time, or – Low turnaround time, or – Avoid starvation 4 Motivation & Objective (cont.) • Be familiar with Nachos – Understand the inner structure – You’ll need to trace in this project • Implement priority scheduling algorithms, combine it with built-in timer to achieve timesharing (Round-Robin) 5 Nachos Threads • Ready queue – A List object: a list of ready thread objects • Thread state – READY • The thread will be kept in readyList. – RUNNING • The global variable currentThread always points the currently running thread. – BLOCKED • The thread is blocked to wait for some event until the event takes place. • A blocked thread is not in readyList. Instead, it would be put in other queues. – JUST_CREATED • The thread is just created, but not ready to be put in readyList (not ready to run). 6 Thread Life Cycle Thread::Thread() JUST_CREATED Note: Thread::Yield() invoks Scheduler::FindNextToRun() to select next thread to run. Thread::Fork() Call Scheduler:: ReadyToRun() READY Event occurs, call Scheduler::ReadyToRun() (Move thread back to the ready queue) (use a readyList) Call Scheduler:: Run() Call Thread::Yield() RUNNING BLOCKED Call Thread::Sleep() (Put thread to waiting queue) 7 Thread Object • Thread() – Constructor: sets the thread as JUST_CREATED status • Fork() – Allocate stack, initialize registers. – Call Scheduler::ReadyToRun() to put the thread into readyList, and set its status as READY. • Yield() – Suspend the calling thread and put it into readyList. – Call Scheduler::FindNextToRun() to select another thread from readyList. – Execute selected thread by Scheduler::Run(), which sets its status as RUNNING and call SWITCH() (in code/threads/switch.s) to exchange the running thread. • Sleep() – Suspend the current thread and find other thread to run – Change its state to BLOCKED. 8 Thread Object (Cont.) • Thread *Thread(char *debugName) – The Thread constructor • Setting status to JUST_CREATED, • Initializing stack to NULL, and • Given the thread name for debugging. 9 Thread Object (Cont.) • Fork(VoidFunctionPtr func, int arg) – Thread creation • Allocating stack by invoking StackAllocate() function • Put this thread into ready queue by calling Scheduler::ReadytoRun() – Argument func • The address of a procedure where execution is to begin when the thread starts executing (The thread’s handler function) – Argument arg • An argument that would be passed to thread handler function 10 Thread Object (Cont.) • void Yield() – Suspend the calling thread and select a new one for execution • Find next ready thread by calling Scheduler::FindNextToRun(). • Put current thread into ready list (waiting for rescheduling). • Execute the next ready thread by invoking Scheduler::Run(). – If no other threads are ready to execute, continue running the current thread. 11 Thread Object (Cont.) • void Sleep (bool finishing) – Suspend the current thread and change its state to BLOCKED • Run next ready thread • Invoke interrupt->Idle() to wait for the next interrupt when readyList is empty – Sleep is called when the current thread needs to be blocked until some future event takes place. • Eg. Waiting for a disk read interrupt • It is called by Semaphore::P() in code/threads/synch.cc. • Semaphore::V() will wake up one of the thread in the waiting queue (sleeping threads queue). 12 Thread Object (Cont..…) • void Finish() – Terminate the currently running thread. • Call Sleep() and never wake up • De-allocate the data structures of a terminated thread • The newly scheduled thread examines the toBeDestroyed variable and finish this thread. 13 Example: create thread • void SelfTest() – Test whether thread implementation works. – Fork a new thread and yield current one to execute it • static void SimpleThread(int which) – A procedure where execution is to begin when the thread starts executing • Command line: nachos -K • In this project, you can trace the above two functions as the starting point. 14 The Flow to Invoke SelfTest() • In main.cc – if (strcmp(argv[i], "-K") == 0) threadTestFlag = TRUE; – if (threadTestFlag) kernel->ThreadSelfTest(); // test threads and synchronization • In Kernel::ThreadSelfTest() currentThread->SelfTest(); // test thread switching >./nachos -K kernel->ThreadSelfTest() Thread::SelfTest() Fork(SimpleThread,1) – In Thread::SelfTest() Thread *t = new Thread("forked thread"); t->Fork((VoidFunctionPtr) SimpleThread, (void *) 1); kernel->currentThread->Yield(); SimpleThread(0); 15 Nachos Scheduler • In code/threads/scheduler.cc, shceduler.h • Decides which thread to run next. • Invoked whenever current thread wishes to give up the CPU. (Eg. Call Yield()) • Default scheduling policy: A FCFS readyList with round-robin fashion – RR is supported by Timer object, which generate interrupt every 100 ticks 16 Nachos Scheduler • ReadyToRun() – Change a thread’s status to READY and put it into the readyList. • FindNextToRun() – Fetch the thread at the front of the readyList. • Run() – Change the state of a thread to RUNNING. – Invoke SWITCH() to switch from the current thread to the selected thread. – If we switch threads because current thread called Thread::Finish(), terminate it. (indicated by the variable toBeDestroyed) 17 Scheduler Object (Cont.) • Scheduler *Scheduler() – The scheduler constructor • Initialize the readyList, which is a List Object. • Set thread status as JUST_CREATED. 18 Scheduler Object (Cont.) • void ReadyToRun(Thread *thread) – Make a thread as ready • Set thread status as READY. • Place it on the ready list. • Invoked by Thread::Fork(), or Semaphore::V(). • Note that ReadyToRun() doesn't actually start running the thread. 19 Scheduler Object (Cont.) • Thread *FindNextToRun() – Select a ready thread and return it • Remove and return the thread at the front of the ready list 20 Scheduler Object (Cont.) • void Run(Thread *nextThread) – Suspend the current thread and switch to the new one. • Set it as “toBeDestroyed” if it is a finished thread. • Save the state of the old thread. • Switch to the next thread by invoking SWITCH and set it as running. – SWITCH is defined in code/threads/switch.s • CheckToBeDestroyed() -- check if the previous thread need to be clean up. 21 Timesharing in Nachos • Alarm object – code/threads/alarm.cc and alarm.h – Nachos create a Timer object which interrupts every “TimerTicks” ticks • code/machine/timer.cc and timer.h • When Timer interrupts, nachos calls its interrupt handler Timer::CallBack(), which resets the timer and call Alarm::CallBack() – Alarm::CallBack() • Call interrupt->YieldOnReturn(), which would yield current thread in Interrupt::OneTick() after all interrupts are handled – “TimerTicks” is defined in code/machine/stats.h, default is 100 22 Timesharing in Nachos (Cont.) • Nachos initialize a Alarm object in Kernel::Initialize() • Alarm generates interrupts every 100 ticks (default) • Nachos yields current thread in Alarm interrupt handler • In effect, no thread could run longer than 100 ticks (unless there is no other threads) 23 Implementation • We have 3 tasks: – Extend Nachos to read our schedule file and create threads – Implement a priority scheduling to replace default FCFS – Round-Robin mechanism is already built by Alarm object, but we can change its time slice • The function names with bold font should be implemented by you 24 Read schedule file • Make Nachos support “-S” parameter. – E.g., > ./nachos -S ParameterFile.txt – When executing Nachos with “-S” parameter, invoke our scheduler testing function: kernel->currentThread-> schedulingTest(param_file_name) 25 Read schedule file (cont.) • We do actual works in a new function in class Thread (Modify code/threads/thread.cc and thread.h) void Thread::SchedulingTest(char *ParameterFile) { <Parse the parameter file to put thread’s name, remaining execution ticks, and priority to ThreadName[], RemainingExecutionTicks[], and ThreadPriorityp[], respectively.> … Thread *t; for(i=0;i<NumberOfThreadsDefinedInParemeterFile;i++) { t = new Thread(ThreadName[i]); t->setPriority(ThreadPriority[i]); t->Fork((voidFunctionPtr) threadBody, (void *) RemainingExecutionTicks[i]); } 26 kernel->currentThread->Yield(); // Give up CPU in order to run new threads } Read schedule file (cont.) • Sample schedule file: 4 A B C D – – – – 2 1 3 2 7 3 5 4 4 threads: A,B,C,D Thread name, priority, remaining ticks Priority = 1~10, 1 is the highest priority Thread name is 10 characters (including NULL) or less, NOTE THAT you should provide a newly allocated space for name strings to Thread constructor because it only stores pointer. DON’T give it local variables. 27 Read schedule file (cont.) • Implement thread body: – We run a while loop, which loops tick_to_exec times. This parameter is supplied by Fork(…, (void*) RemainingExecutionTicks[i]) – Call kernel->interrupt->OneTick() to increase system execution ticks – Print thread info, including its name and remaining ticks void threadBody (int tick_to_exec) { while(tick_to_exec > 0) { tick_to_exec--; kernel->interrupt->OneTick(); printf("%s: remaining = %d\n", kernel->currentThread->getName(), tick_to_exec); } } 28 Priority scheduling • Add required member variables and methods to Thread class (code/threads/thread.cc and thread.h) – void Thread::setPriority(int p) • Set new priority p to this thread – int Thread::getPriority() • Retrieve current priority – int priority • A thread’s priority (private variable) • Modify constructor to give default priority to new threads – Thread::Thread() • In this project, we set default priority to 10 in order to avoid these threads’ effects when doing our scheduling 29 Priority scheduling (cont.) • Modify Scheduler class – code/threads/scheduler.cc and scheduler.h – How to do? Please figure it out yourself – You may need to use SortedList class – If two threads have the same priority, the first thread in the schedule file runs first (FCFS) 30 SortedList • SortedList inherits List class, except that it can insert an “item” into a list in increasing/decreasing order. • When creating a new SortedList object, we have to register a “compare” function, which decides which element is bigger. • SortedList<T>::Insert(T item) – Insert an “item” into a list by invoking the registered “compare” function to decide the place to put the “item” in the list. • This is a template class, which can be used on different data types – Eg. SortedList<Thread*> is a SortedList class which stores pointers to Thread objects – The SortedList uses the “compare” function you implemented to determine which Thread object is “bigger”, or have higher priority 31 Round-Robin • Alarm class generates interrupts every TimerTicks ticks (default = 100) • TimerTicks is defined in code/machine/stats.h • You can change it, and watch the differences 32 Sample scheduling result • Time slice = 100 ticks • Schedule file: 4 A B C D 2 1 3 2 7 3 5 4 • Why thread D run before A? – Timer interrupts B: remaining = 2 B: remaining = 1 B: remaining = 0 D: remaining = 3 D: remaining = 2 D: remaining = 1 D: remaining = 0 A: remaining = 6 A: remaining = 5 A: remaining = 4 C: remaining = 4 C: remaining = 3 C: remaining = 2 C: remaining = 1 C: remaining = 0 A: remaining = 3 A: remaining = 2 A: remaining = 1 A: remaining = 0 Machine halting! 33 Requirement • Implement priority scheduling • Write a 2-page report – Don’t just paste your code, I’ll read it myself – Change time slice to 50 ticks, run nachos on test schedule we supply and explain the results – You may need to use “-d +” to trace the execution • If your project submission can’t compile and execute on Linux in Workstation Room 217, we will consider it as fail. – Please contact me to apply a workstation account if you need it. 34 Test files • We supply a test schedule file “testThreads.txt” on our webpage – Your implementation should have the same results as the sample scheduling when time slice = 100 – We will use other schedules to test, so don’t cheat 35 Submission • Two people in one group (Write down the name and student ID of all members in the report) The file you need to send: • 1. 2. A report in .pdf or .doc threads.cc, threads.h, scheduler.cc and scheduler.h • Send your files tar zcvf os_hw2_bXXXXXXXX_bOOOOOOOO.tar.gz report.doc exception.cc – – – Tar your files to an archieve named: os_hw2_bXXXXXXXX_bOOOOOOOO.tar.gz E-mail to artoo@csie.ntu.edu.tw with following title: [os_hw2] bXXXXXXXX_bOOOOOOOO Please follow the format carefully or our auto36 reply system will not work Submission (cont.) • Deadline: 10/27 24:00 – For the delayed submission, deduct 5 points for each day • DO NOT COPY!! Protect your code well!! 37