Lab 8: Priority Scheduling

The goal of this lab will be to introduce priority scheduling to our OS. This will allow processes to run at different priority levels, giving the user more control over how processing resources are allocated.

The Default Scheduler

Take a look at the scheduler() function in kernel/proc.c to familiarize yourself with how scheduling currently works. The basic algorithm we’ll implement will look like this:

It’s important to run your OS with only one CPU core during this exercise. If you run with multiple cores, then it will be a lot harder to reason about whether the scheduling decisions were made correctly or not. To do this, run:

CPUS=1 make qemu

Note the CPUS=1 part – don’t forget it! You can try doing this now, but you probably won’t be able to see much of a difference since you haven’t made any scheduling changes yet.

Tracking Process Priorities and Niceness

You will need to do some prep work before modifying scheduler(). This includes:

Since you have done all of the above before in your previous labs, I will spare you the long, drawn-out details. Can you add a system call purely from memory now? (I mean your own memory, not your VMs!)

Here’s a small utility that does nothing useful, but DOES do something as far as the CPU is concerned, spinner.c:

#include "kernel/types.h"
#include "user/user.h"

int
main(int argc, char *argv[])
{
  if (argc <= 2) {
    fprintf(2, "Usage: spinner name amount\n");
    exit(1);
  }

  char *name = argv[1];
  int amount = atoi(argv[2]);

  int start = uptime();
  for (int i = 0; i < amount; ++i) {
    printf("%s", name);
    for (int j = 0; j < 10000000; ++j) {
      // wheee :-)
    }
  }
  int end = uptime();

  printf("\n[%s] has finished, %d work in %d ticks.\n",
         name, amount, end - start);

  exit(0);
}

And a script to run it:

#!/sh

nice 0 spinner A 50 &
nice 1 spinner B 25 &
nice 3 spinner C 50 &

(Glad we added script support to the OS…)

Looking at the priorities and processes listed above, what would you predict will be the outcome? (i.e., what finishes first, second, etc.?) How about with the following?

#!/sh

nice 0 spinner A 100 &
nice 0 spinner B 25  &
nice 1 spinner C 50  &
nice 2 spinner D 50  &
nice 2 spinner E 10  &
nice 3 spinner F 100 &

(We’ll refer to these as spin1.sh and spin2.sh, respectively).

Updating procdump

Once you’ve made the required changes, update procdump() in kernel/proc.c so that it will print the numeric process priorities in addition to the existing information already printed out.

Next, run nice 0 ls / and immediately press CTRL+P to see the process list. Verify that ls is correctly set to 3 for its priority (due to having a niceness of 0).

Updating the Scheduler

Finally, we need to make the scheduler aware of priorities. To do this:

You may wonder if this could benefit from a better data structure, maybe a heap. That would indeed be better, if we were dealing with larger process tables. Since we usually have no more than 5 or so processes running at a time, this approach will suffice for now.

Testing

For the most basic test, run two processes with opposite priority levels and observe how long it takes them to complete. Then move on to the spin1.sh and spin2.sh scripts.

And don’t forget:

CPUS=1 make qemu

Grading and Submission

Once you are finished, check your changes into your OS repo. Then have a member of the course staff take a look at your lab to check it.

To receive 75% credit:

To receive full credit for this lab: