BrickOS Scheduling

Download Report

Transcript BrickOS Scheduling

BrickOS Scheduling
Solving The Low-Priority
Starvation Problem
Introduction
Jon Rogers
Robbie Edwards
Melanie Sparks
Project Description
Investigate the way the scheduling
algorithm is currently implemented.
Change it so that higher priority processes
cannot starve the low priority processes.
Project Facets
1 Researching the problem
2 Defining a solution
3 Building a development environment
4 Modifying the source code
5 Testing and verification
Researching The Problem
Research the operating system scheduler
Identify the relevant source code
Determine the current scheduling
implementation
Researching The Problem Results
BrickOS employs a preemptive scheduler
with a prioritized round-robin algorithm for
choosing the next process to run.
Researching The Problem Detailed Results
Each process is given a default time slice
of 20ms to run before being interrupted by
the OS and another process is allowed to
run
In choosing the next process to run, the
OS searches through each process
queue, beginning with the highest priority
queue, and runs the first READY process
Hence The Problem If there are always processes of higher
priority that are ready to run, lower priority
processes will be starved out
Defining A Solution
The Challenge: to prevent starvation while
preserving process priority
The Solution: Chain Wait, another
selection parameter
Chain Wait will numerically represent the
maximum number of scheduler executions
for which a particular priority chain will be
exempt (e.g. no processes from this
priority chain will be chosen: waiting)
The Algorithm
Initialization: All chain wait values will be set
to zero
Scheduler will do a linear search through the
priority chains for runnable processes
Upon running a process, the chain wait for
its priority queue will be set to 20-P+1,
where P is the numeric priority and
scheduler will resume its linear search
The Algorithm
For each priority queue with a chain wait
greater than zero, decrement the chain
wait and move to the next queue ignoring
ready processes from this queue
If the linear search is complete and no
runnable processes are found, all chain
waits are reset to 0
Building A Development
Environment
Installation of Cygwin on the target Windows
machine
Building the Hitachi-H8 cross-compiler : a trivial
initialization followed by a very long compile
process
Installing BrickOS
Installing the USB tower and drivers
Testing the environment :
Hello World and Play Song
Testing the environment, part II : creating a test
program that counts down and plays a song
Changing The Source Code –
Additions To tm.h
Implementation begins with adding a new function,
"tm_clear_waits()", that will (as advertised) clear the chain
waits, resetting them to zero.
void tm_clear_waits() {
pchain_t* priority;
priority = priority_head;
while((int) priority->priority >= PRIO_LOWEST)
{
priority->chain_wait = 0;
priority = priority->next;
}
}
Changing The Source Code –
Additions To tm.c
Implementation of the solution continues with the
introduction of a new field, an integer "chain_wait", to the
existing data structure struct _pchain_t, in the tm.h
header file
struct _pchain_t {
priority_t priority;
// numeric priority level
struct _pchain_t *next;
// lower priority chain
struct _pchain_t *prev;
// higher priority chain
struct _tdata_t *ctid;
// current task in chain
int chain_wait;
// # of times to be ignored by scheduler
};
Changing The Source Code Changes To tm.c
We modified tm.c inside the function "size_t
*tm_scheduler(size_t *old_sp)" at the point
where the next process must be chosen
This scheduler function is called by the process
switcher which in turn is called by a yield
function
Yield is called by an function that becomes a
zombie [through "exit(..)"], when a process has
used its timeslice [through systime_handler(..)],
or when a process sleeps [through
wait_event(..)].
Original Code
The original code, in which we will choose the next process..
while(1) {
if(next->tstate==T_SLEEPING)
break;
if(next->tstate==T_WAITING) {
...
break;
}
}
if(next == priority->ctid) {
...
priority = priority->next;
...
next=priority->ctid->next;
}
else next=next->next;
}
// if the next task is SLEEPING
// we just run it
// if it's WAITING, wake it and run it
// if done searching this priority queue
// search the next priority queue
// move to the next one in line in that priority
// otherwise we're still searching this queue
New Code
Inside the while loop that searches for the next process, the code is
modified as follows:
while(1) {
// If there is a chain wait for this priority level
// then decrement the wait.
if(priority->chain_wait > 0) {
--(priority->chain_wait);
if(priority->next != NULL) {
if((int) priority->next->priority < PRIO_LOWEST)
{
}
// If we have ignored the lowest priority level
tm_clear_waits();
// then clear all chain waits and begin the
priority=priority_head; // search for a process again at the highest
next=priority->ctid->next;
// priority level.
New Code
else {
priority = priority->next;
// Otherwise,
next = priority->ctid->next;
}
//search the next priority level.
}
...
...
}
// debugging code omitted
else priority = priority_head;
New Code
else {
// if the next task is SLEEPING,
break;
// we just run it
if(next->tstate==T_WAITING) {
// if it's WAITING
if(next->tstate==T_SLEEPING)
...
break;
// wake it and run it
}
// sound familiar?
...
// the remainder of the code
// inside the while loop is unmodified
}
}
Changing The Source Code
Upon exiting the while loop that selects the next
process to run, set the process' priority level's
chain wait.
priority->chain_wait = ((int) PRIO_HIGHEST - (int) priority->priority) + 1;
For example, if PRIO_HIGHEST is 20 and the
process to be executed is of priority 10, then the
chain wait for priority level 10 will be set to (2010)+1 = 11.
Changing The Source Code
And the last bit of code added, within the
process "tid_t execi(..)" upon initializing a
new priority queue the chain_wait must
also be set
...
newpchain->priority=priority;
newpchain->chain_wait = 0; // initialize chain_wait to 0
newpchain->ctid=td;
...
Testing And Verification
Loading the new operating system
Documenting the lack of starvation
Showing that all threads run no matter the
priority
Testing And Verification - Code
The test program: gizmo.c
pid2=execi(&motor_driver, 0, NULL, 20,
DEFAULT_STACK_SIZE);
pid3=execi(&foo1, 0, NULL, 20, DEFAULT_STACK_SIZE);
pid4=execi(&foo2, 0, NULL, 20, DEFAULT_STACK_SIZE);
pid5=execi(&foo3, 0, NULL, 10, DEFAULT_STACK_SIZE);
Testing And Verification - Code
In the old brickOS, this process would have
been starved out. But not in ours.
pid6=execi(&playSong, 0, NULL, 1,
DEFAULT_STACK_SIZE);
You know the scheduler is working correctly
when you hear the song playing.
Testing And Verification Results
The song played repeatedly, indicating that
the forked process with priority zero was
not starved out by the higher priority
processes. All processes were shown to
have been scheduled.
Thank You And Good Night