Due Friday, October 21, 6:00pm
This project has two parts:
Adding a new scheduler. You will augment the
existing GeekOS Round Robin scheduling algorithm with your scheduling algorithm.
Your operating system should
be able to select which scheduling
algorithm is being used via a system call: the system call int
Set_Scheduling_Policy(int policy) should be
implemented for this purpose. If the value of policy is 0, the system
should
switch to round robin scheduling,
if the policy is 1,
the system should switch to your scheduler.
Other values of this parameter should result in EINVALID.
When the system boots up, it will have to use your scheduler. So don't
forget to set your scheduler as the initial scheduler.
The choice of which scheduler to use should be made within the
function Get_Next_Runnable() (see kthread.c). Any
function that calls Get_Next_Runnable() should be unaware of
which scheduling algorithm is being used (i.e., do not try to pass the
scheduling type as an argument). It should only be aware that some
thread has been selected.
You are free to create a new scheduling algorithm that is optimized for anythign you want. However, you need to explain what goals it has. With you code, you turn in a REAME.scheduler in the top level of the class project directory. Get System Time. One way to compare scheduling algorithms is to see how long it takes a process to complete from the time of creation to the termination of the process. You will investigate these differences by implementing the Get_Time_Of_Day() syscall.
Get_Time_Of_Day() will return the value of the kernel
global variable g_numTicks. The variable is already
implemented
in the kernel (see timer.h), you only need to implement the
system call to read it.
You can use this system call in a user program to determine how much
time has elapsed while the program was running. You can do this by
calling
Get_Time_Of_Day() once at the beginning of the process
(in the user code) and once at the end. You can calculate how long the
process took to run, as well as when the process first got scheduled
(based
on ticks). Notice that there is no attempt to remove time spent by
other processes. For example, if your process context switches out,
then a
second process runs, the second process's time during the context
switch will be
included in the first process's elapsed time. This is known as "wall
clock"
time. One can also just calculate the time used by the process itself.
This is called "process time" (or sometimes virtual time) but
this project is not concerned with that.
Additional Reading. Scheduling is covered in Chapter 5 of the text. Multi-level feedback scheduling is covered in Section 5.3.6, pps. 168-169.
You will add system calls that provide user programs with
semaphores, to enable thread synchronization among different threads.
The system calls (on the user side) will be:
int Open_Semaphore(const char *name, int ival)
int P(int sem)
int V(int sem)
int Close_Semaphore(int sem)
Open_Semaphore(name, ival) is a request by the current
process to use a semaphore. The user
gives a name for
the semaphore, as well as the semaphore’s initial value, and will get
back an integer semaphore ID.
The semaphore ID denotes a particular
semaphore datastructure in the kernel, which you must implement.
The semaphore ID is then passed by the user program to the operations
P() and V(), described next, to wait or signal the associated
semaphore.
Your operating system must be able to handle at least
20 semaphores whose names may be up to 25 characters
long. If there are no semaphores left (i.e., there were N
semaphores with unique names already given), ENOSPACE must
be returned indicating an error.
The returned semaphore ID is chosen in one of two ways.
The P(sem) system call is used to decrement the value of
the semaphore associated with sempahore ID sem. This operation is
referred to as wait() in the text. Similarly, the V(sem)
system call is used to increment (signal() in the text) the value of
the semaphore associated with sem.
As you know, when P() is invoked using a semaphore ID whose associated semaphore's count is less than or equal to 0, the invoking process must block. To block a thread, you can use the Wait function in the kernel. Each semaphore data structure will contain a thread queue for its blocked threads. Look at kthread.h and kthread.c to see how it is declared and used. To wake up one thread or all threads waiting on a given semaphore, i.e., because of a V(), you can use Wake_Up_One()/Wake_Up() routines from kthread.h.
A process may only legally invoke P(sem) or V(sem) if sem was
returned by a call to Open_Semaphore for that process (and the
semaphore has not been subsequently destroyed). If this is not
the case, these routines should return EINVALID.
Close_Semaphore(sem) should be called when a process is
done using a semaphore; subsequent calls to P(sem) and V(sem) (and
additional calls to Close_Semaphore(sem) by this process) will return
EINVALID.
When a thread exits, the kernel should close any semaphores that the thread still has open. In your code, both the Sys_Close_Semaphore() function and at least some function involved in terminating user threads should be able to invoke the "real" semaphore-closing function.
In this, and other projects, you will rely heavily upon a list data structure. For this reason an implementation has been provided to you in list.h file. Please familiarize yourself with its syntax and functionality. It could be a little tricky to understand the syntax since functions are written using #define. Naturally you are always free to extend, modify, or write your own implementation that would better suit your needs.
Additional Reading.
Semaphores are covered in Chapter 6, Section 6.5, of the text;
pps. 202-204 describe implementation issues.
Identifier | Kernel Function | User Function | Effect |
SYS_SETSCHEDULINGPOLICY | int Sys_SetSchedulingPolicy(struct Interrupt_State* state) | int Set_Scheduling_Policy(int policy) | if policy is 0 or 1 change scheduler; else return EINVALID |
SYS_GETTIMEOFDAY | unsigned long Sys_GetTimeOfDay(struct Interrupt_State* state) | unsigned long Get_Time_Of_Day() | return g_numTicks |
SYS_OPEN_SEMAPHORE | int Sys_Open_Semaphore(struct Interrupt_State* state) | int Open_Semaphore(const char *name, int ival) |
if name is longer than 25 characters, return ENAMETOOLONG.
if a semaphore with this name doesn't exist, create it and return its SID; if it exists, return its SID; note that SID must be >= 0 |
SYS_P | int Sys_P(struct Interrupt_State* state) | int P(int sem) | might block wait() semantics returns EINVALID if sem invalid returns 0 on success |
SYS_V | int Sys_V(struct Interrupt_State* state) | int V(int sem) | never blocks signal() semantics returns EINVALID if sem invalid returns 0 on success |
SYS_CLOSE_SEMAPHORE | int Sys_Close_Semaphore(struct Interrupt_State* state) | int Close_Semaphore(int sem) | never blocks returns EINVALID if sem invalid returns 0 on success |
The files we provided can be used to test your semaphores or scheduling algorithm:
% /c/workload.exe [algorithm]
where algorithm can take any of the values rr or mys
.
% /c/sem-ping.exe &
% /c/sem-pong.exe