Operating Systems Project 3


The goals of this project are:
  1. to familiarize yourselves with the Minix system call mechanism
  2. to familiarize yourselves with the Minix scheduler
  3. to write a simple Earliest-Deadline-First real-time scheduler

This is an individual or group project, based mostly on your choice for project 2. You may collaborate with anybody else and search the web, as long as all the code you turn in is authored by you personally. You may use any libraries that are part of the standard Minix distribution.

The project is due Wednesday, September 29th 2004, any time. Submission is by email. Please send me the following:

  1. a brief description of the status (success or lack thereof) of your project.
  2. a copy of your _setdl.c file from lib/posix/. You will also have to create a setdl.s file in lib/syscall/, but do not send it in.
  3. a copy of your changes (with context, i.e. including two non-empty lines before and after each change) to /usr/include/minix/callnr.h
  4. a copy of your changes (with context) from /usr/src/mm/table.c (you must also change /usr/src/fs/table.c, but do not send in those changes)
  5. a copy of your changes (with context) from /usr/src/kernel/proc.h (you may (or may not) also have to change /usr/src/kernel/sconst.h, but do not send in those changes)
  6. a copy of your implementation of the system call, either the changes from an existing file in /usr/src/mm/, or a new file in /usr/src/mm/.
  7. a copy of your changes (with context) from /usr/include/minix/com.h
  8. a copy of your significant changes to kernel/clock.c.
  9. a copy of your significant changes to kernel/proc.c.

Please send in your project on time -- late submissions will not be accepted, and I prefer to have partially-working projects rather than no project at all.

As usual, you are welcome to study any materials on the web, inlcuding the RT-Minix project. As always, be sure that the code you turn is code that you wrote (and copying does not count as writing).

Description

I want you to add a new system call, setdl, to your Minix memory manager (mm -- the call could go into fs, but I think it belongs better in mm, and in any case please implement it in mm). The system call "setdl (long deadline)" takes as argument a deadline, expressed in ticks from the time of the call, and uses it and the Earliest Deadline First algorithm to decide when to schedule the process. Implementing this system call requires changing the process table entries to record the deadline (if any) for the process, and changing the scheduler (function ready in file kernel/proc.c) to give maximum priority to the user process with the earliest deadline, if any. Processes with deadlines should never have their quantum expire -- this may require some changes in kernel/clock.c. Once a process has reached its deadline, it must call setdl with a new value, either 0 (to cancel the real-time nature of the process), or a new value of the deadline.

The deadline given to setdl is a releative one, and the system must (logically) decrement each real-time process's deadline every clock tick, until the deadline reaches zero, at which point the process is assumed to have completed its work (normally the process will call setdl before then to change the deadline). Note that with this scheme the system has to decrement the deadline for each realtime process on every clock tick. A more realistic implementation might add the given deadline to the realtime variable and store the resulting value in the process table. This means the scheduler can simply compare deadlines when inserting a process into the ready queue, and never has to change or decrement the value.

An example of the use of setdl is as in the following code (which I call testrt.c), which expects to execute its main loop at least every second for one hundred seconds.

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <time.h>

extern int setdl (long); /* system call */

void testrt (void)
{
  time_t recent, new;  /* times */
  int count;

  setdl (120); /* let all other RT processes start up */
  sleep (1);   /* while we sleep */
  recent = time (NULL);
  count = 0;
  while (count < 100) {
    while ((new = time (NULL)) == recent) { /* still on the same second */
      setdl (60);               /* check time again within one second*/
    }
    if (new == (recent + 1)) {  /* new second: did we skip any? */
      count++;                  /* no -- still working in realtime */
    } else {                    /* missed the deadline */
      setdl (0);                /* no longer real time */
      printf ("missed deadline after %d seconds\n", count);
      break;
    }
    recent = new;
  }
  setdl (0);                    /* no longer real time */
  printf ("successfully met %d successive deadlines\n", count);
}

int main (int argc, char ** argv)
{
  testrt ();
}


In the absence of competing processes, this process can be expected to execute fine even without a real-time scheduler, so be sure to test this by running at least two (I have run up to ten) copies of the code simultaneously. In all my tests so far, in the absence of a real-time scheduler, at least one of the processes fails, and the last process to remain running will complete all iterations.

Real-time user processes should have the highest priority of all user processes, but should not pre-empt tasks or servers -- if you give any user process higher priority than tasks or servers, (a) you will have to do a lot more work, and (b) your system calls will stop working. It is therefore a good idea to keep executing system code before user code.

The files that you must turn in are only a subset of the files you will have to modify. In particular, you will have to study and perhaps modify mm/param.h to figure out how to pass parameters to a system call in the memory manager.

Some hints

Skip this section if you like to figure things out yourself.

Most of these are drawn from my own experience and mistakes implementing this project. If you make mistakes, don't worry -- it is very normal. Instead, just figure out what the problem is, and fix it.

You must implement a library function (in library/posix and library/syscall) which sends a message to a system call, setdl, implemented in mm. The message may or may not contain the process number of the process requesting a change in deadline (you may call getpid(2) from _setdl.c if you need to get the process ID in user code), and you will have to send the number of clock ticks until the deadline expires (this is the parameter to setdl) This means you must select one of the 6 possible minix messages (see /usr/include/minix/type.h). For a system call, you will also need a new system call number.

After you have modified the library (to add the new syscall), be sure to make install from /usr/src/lib. You may want to back up your libraries first if there is any possibility you might be breaking the library.

A system call in mm cannot directly access the variable realtime, nor can it add anything to kernel component of the process entry. Therefore, setdl must send a message to the clock task asking it to set the deadline for this process. Therefore, as well as modifying the scheduler, you will have to change the clock task appropriately.

This message sent by the system call in mm (with its own new message number in /usr/include/minix/com.h) contains essentially the same value(s) as the message that your system call received. This message is handled by the clock task, which typically receives a message, then sends a reply. There is no reason why your system call should require a reply (it always succeeds :-), so you may need to change the if statement at the end of the clock task loop so no reply is sent for your message -- or, alternately, you may have your system call (in mm) read a reply, if you prefer. If you do none of the preceding, and the clock task tries to send a message to your system call and your system call is not going to receive the reply, your operating system will crash very nicely (I can tell you from experience :-).

Once you have implemented the basic system call mechanism and gotten a message to the clock task (and you can use printf/printk to check -- but never use these functions in the part of the scheduler which may execute at interrupt time, or "locked"), the fun part (implementing the real-time scheduler) can begin. Until that point, however, it is unwise to start working on the scheduler. As part of the process of debugging the system call thoroughly, you can use F1 to print process IDs of running processes, or use "&" at the end of the command to put the command in the background and print its process ID. In short, make sure you are passing in the correct deadline and for the correct process.

Now you must modify the process table to record the deadline, and the function ready so the process with the least deadline will be executed first. It is good to remember that:

If you try to call lock_ready from the clock task, your system will crash in very interesting ways (another personal experience :-). The process for which you are setting the deadline is suspended waiting to receive its reply, and making it ready will (I guess) result in its being placed twice on the ready queue. I am not quite sure why this would cause a general protection fault, but I can tell you that it does. In short, once you have changed the deadline, you can let existing calls to ready reschedule the process, and you should not directly call lock_ready from the clock task.

There are many ways of doing this project, and you should feel absolutely free to deviate from any of these suggestions.