CPSC 331 Operating Systems Spring 2026

Project 4
Lottery Scheduling

Due: Wed 4/8 at the start of class

This project has several goals:

Your primary task is to implement lottery scheduling for xv6, as described in the project README and below. As part of this, you will add two system calls to support lottery scheduling and write a simple version of the utility ps. There again isn't a lot of code to write; as with the previous project, the crux of the assignment is understanding what is going on in the relevant portions of xv6.

Collaboration and the Use of AI

Review the course policy on academic integrity.

Certain uses of AI are permitted on this assignment. AI use is not required.

The basic rule: you may use the code completion features of Copilot but may not use features (such as the coding agent) where code is generated from English language prompts. It is also essential that you understand and think critically about any code suggested by Copilot, both to help develop your C programming skills and because while the code suggestions are often uncannily on target, they are not always exactly what you want.

Using the code explanation features of Copilot Chat is permitted, though be careful that this doesn't spill over into code generation.

Other Policies

Review the policy on late work.

Revise and resubmit applies for this assignment. Review the details of how revise and resubmit works.

Handin

To hand in your project:

Check that the handin was successful and that the directory structure is correct: your handin folder /classes/cs331/handin/username should contain a folder scheduling-xv6-lottery which in turn contains a src directory.


Preliminaries

Do the steps outlined in reddish boxes below before you start writing code.

Provided Code

The provided code includes the xv6 codebase (the same thing you started with for the last project with a few additions described below), some test cases for the system calls you'll add, and a tester program user/schedtest.c to aid in determining if your scheduler is working correctly.

VSCode Configuration

Very important! One of the things the autoformatter can do is reorganize the #include directives in your code. While useful in some circumstances, this will break the xv6 code. You should have taken care of this as part of the setup for the previous xv6 project. (If you didn't, go back and look at that now.)

Testing

The provided test cases focus on the two system calls you are implementing. Running them is largely the same as with the previous xv6 project, with the one difference being that if you want to run the provided tests directly (instead of using make test), do so with:

    ./test-lottery.sh

from the top-level scheduling-xv6-lottery directory.

In addition, a test program schedtest has been provided to help you test your scheduler, but this will need to be run manually. From the xv6 shell prompt in QEMU, run either:

  schedtest <n> <ticks> <tickets1> [<tickets2> <tickets3> ...]

or

  schedtest <n> <ticks>

where n is the number of processes to start, ticks is the duration (in ticks, which are approximately 10ms each) to let the processes run, and tickets1 etc are the number of tickets assigned to process 1, process 2, and so forth. The second version runs the n processes with the same (default) number of tickets. (This is handy for observing the default round-robin scheduler, or to test that the lottery scheduler acts like a round-robin scheduler when all processes have the same number of tickets.) Note that you do not type the <> or [] symbols — <> denotes something you replace with an actual value and [] denotes optional elements.

Important: schedtest makes use of the getpinfo and settickets system calls that you will be implementing, so you cannot use it (or even compile it) until you've completed those steps. As a result, the provided Makefile does not compile schedtest.c. When you are ready to use schedtest, look for the following lines in src/Makefile:

XUPROGS := 
#XUPROGS := schedtest  

and replace them with

#XUPROGS := 
XUPROGS := schedtest  

(This just switches which line is commented out.) Run make to compile and you should be good to go.

Reference

xv6

Of particular relevance to this project:

Formatted Output

xv6's printf is much more limited than the standard C version. To help you line things up in columns, a padint function has been added to xv6's standard libraries. Locate its implementation in the xv6 code to see what it does and how to use it. As with printf, you'll need to #include "user.h" in any code that uses padint.

Random Numbers

You'll need a random number generator for lottery scheduling, but you don't need to hunt down and implement your own as stated in the README — versions of C's standard rand and srand functions have been added. Look up the C versions in the man pages (man 3 rand) to find out how to use them, but #include "rand.h" instead of the standard C library when you use them in your code.

Debugging

To print messages for debugging kernel code, use cprintf instead of printf. (printf is only for printing when running in user mode — the two different versions have to do with using different sections of the address space for kernel vs user mode.) cprintf is similar to printf but you don't have to specify the file descriptor first — all output from cprintf goes to the console. Look for where it is defined/used in the xv6 code for examples.


Specifications and Details

You have one main task: implement lottery scheduling. There are also two supporting tasks: add some additional process statistics and a ps command to help you see whether or not your scheduler is working properly. The project README provides an overview, though note that your implementation should follow what is described below — there are some modified specifications as well as more specifics about how to achieve what the README describes.

To do this:

getpinfo and ps

The first step is to add support for getting information about processes — a system call int getpinfo(struct pstat *) to retrieve the info from the OS and a user command ps that can be run from the shell. (This isn't needed for the scheduler as such, but it will make it possible to see whether the scheduler is working correctly.) To do this:

pstat

Definition for pstat:

#ifndef _PSTAT_H_
#define _PSTAT_H_

#include "param.h"

struct pstat {
    int inuse[NPROC];             // whether this slot of the process table is in use (1 or 0)
    int pid[NPROC];               // the PID of each process
    char name[NPROC][16];         // the name of each process
    int state[NPROC];             // the state of each process (as int because enum procstate is not visible in user space)
    uintp sz[NPROC];              // the size of each process's memory (bytes)

};

#endif  // _PSTAT_H_

System Calls With Parameters

As with getreadcount, it is helpful to look for an example already in the code that is similar to what you want to do. Since you are implementing a system call, look over the headers in include/user.h to find anotehr system call with similar parameters to use as a guide — getpinfo takes a struct, so fstat (which takes a different struct) is a good example.

Looking at the body of sys_fstat, you'll see that it does...something...and then calls a helper function filestat. filestat does the real work of the system call; the something before that call is what deals with the system call's parameters. Remember the flow of control when a user program calls a library routine: the handling of the subroutine call involves pushing the subroutine's arguments onto the stack. So that pstat (well, the address of the pstatstruct pstat * means we've passed a pointer) is on the stack, and the body of sys_getpinfo needs to retrieve it. Also, since we're now in kernel mode, the OS should thoroughly check that the parameters are valid. That's what is going on in sys_fstat — a local variable is declared for each of the system call's parameters and argfd, argptr, and argint (not used by sys_fstat) retrieve various types of parameters from the stack into local variables. Read more about this in the following (start with the section on "fetchint" and continue through "argstr").

Follow a similar pattern for getpinfo — retrieve the parameter values and do basic validity checking in the body of sys_getpinfo (return -1 if there is an error), then call a helper to do the real work. The helper should be implemented in kernel/proc.c — if you look for where filestat is implemented, you'll notice that the convention for file-related system calls is to put the sys_ handler in kernel/sysfile.c and the helper in kernel/file.c. Process-related system calls go into sysproc.c and proc.c.

Implementing getpinfo

Finally, on to the actual functionality of getpinfo! First check any preconditions (e.g. that the parameter passed in isn't NULL), then go through the processes in the process table (ptable.proc) and copy the relevant information into the pstat parameter. The return value should follow the usual conventions: return -1 if there is any error (such as a violated precondition) and 0 on success. Some notes and tips:

Tracking Ticks

To help determine whether or not a scheduler is working properly, we need to be able to determine how much CPU time a process is getting.

Tickets

Lottery scheduling is based on tickets, so we need to add support for tickets.

Lottery Scheduling

Finally, scheduling.

Some notes and tips on implementing lottery scheduling: