CPSC 331 Operating Systems Spring 2026

Project 4
xv6 Scheduler

Due: Mon 4/6 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

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

Running xv6 and the Provided Tests

This is largely the same as with the previous xv6 project. The one difference is 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.

The provided test cases focus on the system calls. A test program schedtest is provided to help you test your scheduler, but this will need to be run manually. From the xv6 shell prompt in QEMU:

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

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. Note that you do not type the <> or [] symbols — <> denotes something you replace with an actual value and [] denotes optional elements.

Reference

xv6

Of particular relevance to this project:

New Library Routines

Headers for the available user library routines can be found in include/user.h.


Specifications and Details

You have one main task: implement lottery scheduling. 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:

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.

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

Tickets

Lottery Scheduling