CS3013 Project 1

Background Process Scheduling

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.


Index


Description

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:

  1. Performs some basic periodic tasks, like handling interrupt service routines (not a concern of this project)
  2. Chooses one process to execute according to the scheduling policy (the core concern for this project)
  3. Dispatches the chosen process to run

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.


Evaluation

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:

  1. Run your counter as a normal process. Record how long it takes to count.

  2. Run your counter as a SCHED_BACKGROUND process. Record how long it takes to count.

  3. Run your counter simultaneously with another counter, both as normal processes. How long does it take to count?

  4. 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?

  5. 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?

  6. 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?

  7. 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?

  8. 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.


Hints

Implementation

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:

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.

Evaluation

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.


Hand In

You must hand in the following:

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

Return to 3013 Home Page

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.