Due date: Thursday, September 8th by 11:59pm
The short-term processes scheduler is the core of the operating system. It is responsible for choosing a new process to run whenever the CPU is available. You will study parts of the Linux scheduler in depth and understand how it decides on which process to run. You will then modify the Linux scheduler to implement a new scheduling class, called background scheduling. You will evaluate how your new scheduling class performs, and then writeup the details on the whole thing and turn it in.
The scheduler is a kernel function called schedule()
,
that gets called from other system call functions (usually when
a process blocks waiting for I/O), after every system call
and after some interrupts. When invoked, the scheduler:
The Linux scheduler contains 3 built-in scheduling strategies:
SHED_FIFO, SCHED_RR and SCHED_OTHER. The SHED_FIFO and SCHED_RR
schedulers are primarily used for
real-time scheduling, where a process has time deadlines and
requires some guarantee on how soon it will be dispatched. The
SCHED_OTHER policy, the default, uses a conventional time-slicing
method (similar to round-robin, as presented in class) to put
an upper bound on the amount of time that a process will use the CPU.
For SCHED_OTHER, a dynamic priority is computed based on a fixed
priority and the amount of time a process has been waiting for the CPU
to become available. The
counter
and
priority
fields in the process control block are the key
components in determining the task's dynamic priority. The dynamic
priority is adjusted on each timer interrupt.
You are to add a new scheduling policy called SCHED_BACKGROUND that is designed to support processes that only need to run when the system has nothing else to do. This "background" scheduling policy only runs processes when there are no processes in the SCHED_OTHER, SCHED_RR or SCHED_FIFO classes to run. When there are more than one SCHED_BACKGROUND process ready to run, they should compete for the CPU as do SCHED_OTHER processes.
After you have implemented your scheduling policy and debugged it carefully, you will then evaluate its performance by running combinations CPU-intensive processes under different scheduling policies and measuring how long it takes.
The basic CPU-intensive process you will use runs a simple program that counts up to a large number. It should count high enough for this process to require 15 seconds or so to complete its counting (a count to about 3000000000 on a typical Fossil client).
You can use the gettimeofday()
to measure the
"wallclock" time and getrusage()
to record the user and
system time. Your analysis should include wallclock, system and user
time in milliseconds (there are 1000 milliseconds in a second and
1000000 microseconds in a second).
You are to evaluate performance under the following conditions:
Run your counter as a normal process. Record how long it takes to count.
Run your counter as a SCHED_BACKGROUND process. Record how long it takes to count.
Run your counter simultaneously with another counter, both as normal processes. How long does it take to count?
Run your counter as a SCHED_BACKGROUND process, simultaneously with another counter that is running as a normal processes. How long does your SCHED_BACKGROUND process take to count?
Run your counter as a SCHED_BACKGROUND process, simultaneously while
compiling the kernel (do a make clean; make
). How
long does it take to count?
Run your counter as a SCHED_BACKGROUND process, simultaneously with
another counter that is running using nice
(do a
man nice
for more information) at the
lowest priority. How long does your SCHED_BACKGROUND process take to
count?
Run your counter as a SCHED_BACKGROUND process, simultaneously with
another counter that is running using nice
at the
highest priority (you will need to use sudo
to do
this). How long does your SCHED_BACKGROUND process take to count?
Repeat steps 1-3, but this time replace your counter with a process that reads/writes to a large file(s) (the running time should be about 15 seconds).
You are to provide a brief (1 sentence to a small paragraph) text
explaining each of your answers for 3-7 above. Your analysis should
include an interpretation of your results (describe how effective your
SCHED_BACKGROUND class works, in your words, backed up with some
numbers). You should explain discrepancies between the
getrusage()
time reported and the
gettimeofday()
(wall clock) time reported. And describe
briefly how you think nice
works in terms of the Linux
scheduler.
Feel free to run other mixes of processes to examine how your scheduler works.
The system call sched_setscheduler()
is used to change
the scheduling policy for a process. Do a man
sched_setscheduler
for details. For the third parameter to the
struct sched_param
is defined thus:
struct sched_param { int sched_priority; };
You will primarily modify kernel/sched.c
to implement
your new scheduling policy. Use a search routine in your editor
(emacs, pico, vi or whatever) to find code in that file. You can
ignore any code between #ifdef SMP
and the corresponding
#endif
sections, as these sections pertain to
multiprocessor machines, which we do not have in the Fossil lab. The
primary functions you will be concerned with are:
goodness()
to adjust the weight computed
to favor SCHED_OTHER processes (and SCHED_RR/SCHED_FIFO)
over SCHED_BACKGROUND processes
setscheduler()
to allow user processes to
set themselves to be of the new SCHED_BACKGROUND type
scheduler()
the scheduler proper, maybe,
to determine when to recalculate counters.
For completeness, you could modify
sys_sched_get_priority_max()
and
sys_sched_get_priority_min()
but use of those functions
is not required for this project.
You will need to modify include/linux/sched.h
to add
the SCHED_BACKGROUND scheduling policy. Note that sched.h
has
a lot of files depending upon it, meaning there will be a lot that
need recompilation every time you modify it. Change
sched.h
as few number of times as possible. Also, system
programs that have #include <linux/...>
headers may
need to be modified. By default, /usr/include/linux
is
linked to /usr/src/linux/include/linux
. If you modify
any headers under /usr/src/linux/include/linux
those
changes will be immediately usable by user level programs. If you are
modifying include files in a re-named kernel tree, you may need to
adjust your /usr/include/linux
link or duplicate the
changes into /usr/src/linux/include/linux
.
The process control block in Linux called struct
task_struct
and is found under the file
include/linux/sched.h
. The fields counter
and priority
may be useful. You can also add fields to
this structure, if needed.
When writing kernel code, you will want to print messages to
stdout
, as you do when using printf()
or
cout
. Since many parts of the kernel may not have access
to the stdio library, kernel developers wrote their own version of
printf()
called
printk()
. printk()
basically behaves the
same as printf()
, in terms of formatting. Furthermore,
printk()
also writes messages to the log file
/var/log/messages
, so you can view output there in case
your modified OS crashes. You might add prefixes to your printk()
messages, such as "MLC: " or "Fossil: " so you can more easily pick
out your messages from the log file. Be careful! If you have
printk()
messages in a part of the kernel that is
accessed frequently (like the scheduler) it can fill up your log file
quickly. When this happens, your system can become unstable. Check
the size of your log file (using ls -l
) and the disk
space that is free (using du
) frequently.
You are free to modify other components as well, such as the process control block. Study the process and process scheduling code carefully. Aim for a solution with as little impact as possible, to make debugging easier. Don't worry about performance in your first pass. Rather, try a solution that is easy to implement. Once it works, you can re-consider performance from the design perspective if you wish.
You are advised to take a conservative, incremental strategy for
developing your scheduler. First focus on getting some
printk()
statements into some of the key parts in
kernel/sched.c
to build up confidence as to where to add
your modifications. Then, modify sched.c
so you can
change the scheduling class of a process from SCHED_OTHER to
SCHED_BACKGROUND. Then, figure out a way to modify the
goodness()
function to have SCHED_BACKGROUND processes run at
the appropriate time. Once you have that in place and debugged,
concentrate on the evaluation.
Remember to save your work frequently in case you crash your machine! Refer to fossil.wpi.edu for more information on general use of the Fossil lab and other useful Linux links.
The system call setpriority()
is the system call which
sets a processes priority (for "nice-ing" the counter processes, used
in the evaluation below). Do a man setpriority
for
details on how to use it. You can also use nice
, either
as a system program or as a built-in shell command (do a which
nice
to see if the nice you have is built in or part of the
system).
The call getgrusage()
takes as a parameter a pointer
to a struct rusage
which is defined as follows:
struct rusage { struct timeval ru_utime; /* user time used */ struct timeval ru_stime; /* system time used */ long ru_maxrss; /* maximum resident set size */ long ru_ixrss; /* integral shared memory size */ long ru_idrss; /* integral unshared data size */ long ru_isrss; /* integral unshared stack size */ long ru_minflt; /* page reclaims */ long ru_majflt; /* page faults */ long ru_nswap; /* swaps */ long ru_inblock; /* block input operations */ long ru_oublock; /* block output operations */ long ru_msgsnd; /* messages sent */ long ru_msgrcv; /* messages received */ long ru_nsignals; /* signals received */ long ru_nvcsw; /* voluntary context switches */ long ru_nivcsw; /* involuntary context switches */ };
Do a man getrusage
for more information.
The definition for the struct timeval
used above is:
struct timeval { long tv_sec; /* seconds */ long tv_usec; /* microseconds */ };
Do a man gettimeofday
for more information.
You must hand in the following:
sched.c
and sched.h
files).
The turnin (/cs/bin/turnin
) for proj1 is "proj1".
When turnin, also include file "group.txt" which contains the
following:
group_name login_name1 last_name1, first_name1 login_name2 last_name2, first_name2 ...
Also, before you turnin tar up (with gzip) your files. For example:
mkdir proj1 cp * proj1 /* copy all your files to submit to proj1 directory */ tar -czf proj1.tgz proj1
then:
scp proj1.tgz login_name@ccc:~/ ssh login_name@ccc /* will ask your ccc passwd */ /cs/bin/turnin submit cs3013 proj1 proj1.tgz
Send all project questions to the cs3013-staff at cs.wpi.edu mailing list.
Send all Fossil administrative questions to the fossil at cs.wpi.edu mailing list.