Pramod Rao B
2020111012
Implemented a system call strace
,
strace mask command [args]
- Runs the specified
<command>
with the specified arguments[args]
- Traces the
i
th system call if and only if the bit1 << i
(or, equivalently2^i
) is set inmask
. That is, ifmask & (1 << i) == 1
Steps taken to achieve this:
- Added a new variable
traceMask
in the process structurestruct proc
defined in the fileproc.h
which is initialized to 0 so that no bit is set - Added a function
sys_trace
insysproc.c
which acts as a wrapper for the actual system call. It calls the functiontrace
with the first argument representing the mask. - Added a function
trace
inproc.c
which sets thetraceMask
variable of the current mask to the argument supplied. - Modified the
fork
function inproc.c
to copy the parentstraceMask
onto the child - Added a declaration for
trace
indefs.h
and a declaration forsys_trace
insyscall.c
- In order to know the number of arguments for each system call, added a table entry for each system call which stores its name and the required number of arguments
- Added new entries for the
trace
system call insyscall.h
- Modified the
syscall
function insyscall.c
to print the traced information if the bit set condition is met. Additionally make sure to save the value stored in the first registera0
before executing the system call since the return value if overwritten ina0
- Created a new user program
strace.c
in the user directory which calls thetrace
function with the mask argument and then proceeds to callexec
to execute the given<command>
- Added an entry
$U/_strace
in the Makefile to enable compilation of the user program and a declaration fortrace
inuser.h
- Added a stub entry for
trace
inuser.pl
to call the appropriate syscall
The default scheduler of xv6 is round-robin-based. The modified xv6 contains 3 other scheduling policies such that the kernel shall only use one scheduling policy which will be declared at compile time using the SCHEDULER
macro
Use SCHEDULER=X
while compilation to use one of the following scheduling policies
- FCFS: First Come First Serve
- PBS: Priority Based Scheduling
- MLFQ: Multilevel Feedback Queue
Features:
A non-preemptive scheduling algorithm which picks the process which arrived the earliest among all runnable processes
**Steps taken to achieve this: **
- Added an entry in the Makefile for defining the macro
FCFS
- Added a new integer variable
creationTime
in the process structurestruct proc
defined inproc.h
which is set to the currenttick
value when created (inallocproc()
function) - For the scheduling, iterated through the array of processes checking for
RUNNABLE
processes. When found, if the process at hand has a lowercreationTime
than the current minimacreationTime
, then release the old lock while holding onto the current process lock. We then context switch to the held process if any. - In order to make the scheduling non-preemptive, disabled the timed interrupt (
which_dev == 2
) inusertrap()
andkerneltrap()
functions intrap.c
Features:
A non-preemptive scheduling algorithm which picks the process with the lowest dynamic priority. To break ties, we pick the process which has been scheduled lesser number of times. To further break ties, we pick the process with the earlier creation time.
The dynamic priority of a process is an integer in the range [0, 100] and is calculated as,
DP = max(0, min(SP - niceness + 5, 100))
where,
-
Static pririty (SP) is an integer in the range [0, 100] and is set to 60 by default during creation of a process
-
niceness
is an integer in the range [0, 10] is calculated as,niceness = (ticks spent in sleeping state) * 10 /(ticks spent in (running + sleeping) state)
Note: The ticks mentioned above are measured since the last time the process was scheduled
Steps taken to achieve this:
- Added an entry in the Makefile for defining the macro
PBS
- Added a new integer variables
totalRTime, numSched, prevRTime, prevSleepTime
in the process structurestruct proc
defined inproc.h
which is set to the currenttick
value when created (inallocproc()
function). They measure the total runtime of a process since creation, the number of times it was scheduled till now, total number of ticks spentRUNNING
since the last time it was scheduled andSLEEPING
respectively. - For the scheduling, iterated through the array of processes checking for
RUNNABLE
processes. When found, if the process at hand "is better" (that is, has higher priority including all the tie breaks as mentioned above) than the current best process, then, we release the old lock while holding on to the new lock. We then context switch into the held process if any. - Set the
prevRTime
andprevSleepTime
variables to 0 everytime the process is scheduled and similarly incrementnumSched
- To update the time fields, we call the function
update_runtime
defined inproc.c
which iterates over the process table and increments thetotalRTime, prevRTime, prevSleepTime
variables as needed. - We use the
get_DP
function (defind inproc.c
) to calculate the dynamic priority of the given process. We initalizeprevRTime
to -1 indicating that the process has not been run a single time (or that theset_priority
system call has been called as we shall see later) and if so, considers theniceness
to be 5 (the default value). If not, it uses the aforementioned formula - In order to make the scheduling non-preemptive, disabled the timed interrupt (
which_dev == 2
) inusertrap()
andkerneltrap()
functions intrap.c
setpriority new_priority pid
Features:
Added a new system call setpriority
which takes in two arguments, the new priority and the pid of the process to be affected and sets the static priority to the given value if the process has been found.
Steps taken to achieve this:
- Added the declarations (for
set_priority(), sys_set_priority()
) and user program (setpriority()
) by following the steps followed in specification 1 for thestrace
system call - Iterate through the process table searching for a process with the given pid. If found, set its static priority to the supplied value.
- Additionally, we set the
prevRTime
variable to -1. This way, theget_DP
functions calculates theniceness
of the process the next time as 5, which is the default expected behaviour ofsetpriority
Features:
A preemptive scheduling algorithm which uses queues of varying priority to keep track of processes. Lower priority processes get more time slices but can also be preempted when a process with higher priority enters the queue.
The MLFQ scheduling policy has 5 (variable NMLFQ) queues of levels 0 through 4 representing the highest to lowest priority queues. When a new process enters the system it gets added to the highest priority queue. It is then assigned a time quanta specific to its queue level (1 << i ticks for level i). After the time quanta, if the process happens to exhaust it, it implies that the process is CPU bound and hence, is moved to the queue which is one lower in priority. If not, then the process stays in the same queue.
Steps taken to achieve this:
- Created [NMLFQ = 5] number of queues and added basic queue functionality
- Added variables
qLevel, timeSlice, qEnter, timeSpent[NMLFQ]
in process structure inproc.h
to store the priority level, time slice for that level, the time at which the process enters the queue.qLevel
is initialized by 0,timeSlice
to 1,qEnter
to creation time, andtimeSpent
to 0. - For the scheduling, iterate through the process table looking for runnable processes. If found, insert it into the appropriate queue according to the
qLevel
variable. - Going from level 0 to NMLFQ, get the first process that needs to be run.
- We update the
timeSpent[i]
variable at every clock interrupt in theupdate_runtime
function for the current level of the process. - We set the variable
qEnter
to the tick value whenever the process is scheduled - We set the variable
timeSlice
to 1 << qLevel whenever the process is scheduled - In
trap.c
inkerneltrap
andusertrap
, after every timer interrupt check if the process exhausted its time slice. If so, then increment its level (thereby decreasing priority) by 1 andyield()
to reschedule. If not, then the process may stay in the same queue for now. Additionally, check if any queues before the current level has any processes in it. If so, then those processes need to be executed before the current process. Soyield()
to let the rescheduling take over. - By way of implementation, processes in a certain level are done via round-robin basis since we iterate through
In MLFQ scheduling, a process that voluntarily relinquishes control of the CPU leaves the queuing network and when the process becomes ready again, is inserted at the tail of the same queue from which it was relinquished earlier. This feature could be exploited by a process
A process might voluntarily relinquish control to the CPU just at the end of its time slice and immediately go into the ready state again. This helps the process masquerade as an I/O bound process thereby making the CPU let it stay in its high priority queue without being moved to a lower priority queue.
procdump
is a function (present in kernel/proc.c
) that prints a list of processes to the console when a user types ctrl-p
on the console. The functionality of procdump
has been extended to print additional information for other scheduling policies.
Prints the following information about the processes
- PBS:
- PID of the process
- Priority: Dynamic priority of the process obtained using the
get_DP
function which calculates as mentioned in the PBS section - State of the process ("sleep", "runble", "run" or "zombie")
- Total runtime of the process which is stored in the variable
totalRTime
- Total wait time of the process which is calculated as
total - totalRTime
(where,total = ticks - creationTime
which represents the total ticks elapsed since the process was created) - Number of times the process was scheduled (nrun) which is stored in the variable
numSched
- MLFQ:
- PID of the process
- Priority: Level of the queue of the process (-1 if the process is not in any queue) which is obtained by checking the variables
inQueue
and if it is set to 1, then reading fromqLevel
- State of the process ("sleep", "runble", "run" or "zombie")
- Total runtime of the process which is stored in the variable
totalRTime
- Total wait time of the process which is calculated as
total - totalRTime
(where,total = ticks - creationTime
which represents the total ticks elapsed since the process was created) - Number of times the process was scheduled (nrun) which is stored in the variable
numSched
- q_i (5 entries for 5 levels of the queue) representing the total ticks the process has spent in the
i
th queue which is stored in the variabletimeSpent[i]
Note: We observe the priority being -1 for multiple processes. This is the expected behaviour. This is because, when a process is runnable, it might mean that the process is in the proc table but has not been picked into any of the queues. If a process is sleeping, then by definition it has been popped from the queue and is sleeping so has not been added to the queue yet.
The schedulertest
user program was run (three times and then averaged the results) with the CPU
variable set to 1 to obtain the following running and waiting times.
Scheduling policy | Average run time | Average wait time |
---|---|---|
Default Round-Robin (RR) | 21 | 186 |
First Come First Serve (FCFS) | 43 | 196 |
Policy Based Scheduling (PBS) | 21 | 153 |
Multi-Level Feedback Queue (MLFQ) | 22 | 177 |
We see that the running time for FCFS is higher than the other policies by a significant amount. This is justified because in FCFS, we do not introduce I/O bound processes in schedulertest and so, since it has more number of CPU bound processes it increases the average run time.
We also observe that PBS gives the lowest wait time. We would expect MLFQ to have lower wait times because it takes into account more factors than PBS. However, using the array-based queue implementation and iterating through the process table and the queue to find the process to run causes a lot more overhead. If implemented using a different data-structure perhaps the MLFQ would perform better. But as of the current implementation, PBS clearly performs better with a much lower average waiting time.