Contents
- 1 Your Task
- 2 Some Supplied Test Programs
-
3 Hints
- 3.1 Reading on xv6’s scheduler
- 3.2 Reading on Lottery Scheduling
- 3.3 Suggested order of operations
- 3.4 Tracking the number of times a process is scheduled
- 3.5 Adding settickets
- 3.6 Adding getprocessesinfo
- 3.7 Adding the lottery scheduling algorithm
- 3.8 Processes not scheduled enough?
- 3.9 Identifying panics
- 3.10 Note on random bias
- 4 Credit
Changelog:
- 3 Feb 2020: (while still tentative) add example of
lotterytest.c
failure output with explanation - 3 Feb 2020 23:55: (while still tentative) correct typos in
lotterytest.c
messages - 4 Feb 2020: (while still tentative) correct stray mentions of “ticks” instead of “times scheduled” in hints and rearrange hints re: processes not being scheduled enough
- 4 Feb 2020: correct “tick count” in hints to “times scheduled”
- 9 Feb 2020: clarify in “Your Task” that
settickets
only affects the current process. - 10 Feb 2020: correct
processesinfo
toprocesses_info
Your Task
- Add support for tracking the number of “tickets” in a process:
- adding a system call called
settickets
which sets the number of “tickets” for the current process with the following prototype:int settickets(int number)
. The first process should have 10 tickets. You can assume that the maximum number of tickets per process is 100000. The number of tickets should be inherited by children created viafork
.
- adding a system call called
-
Add a system call called
getprocessesinfo
with the following prototypeint getprocessesinfo(struct processes_info *p);
where
struct processses_info
is defined as:struct processes_info { int num_processes; int pids[NPROC]; int times_scheduled[NPROC]; // times_scheduled = number of times process has been scheduled int tickets[NPROC]; // tickets = number of tickets set by settickets() };
(
NPROC
is a constant defined inparam.h
)The system call should fill in the
struct
pointed to by its arguments by:- setting
num_processes
to the total number of non-UNUSED processes in the process table - for each i from 0 to
num_processes
, setting:pids[i]
to the pid ofi
th non-UNUSED process in the process table;times_scheduled[i]
to the number of times this process has been scheduled since it was created (don’t adjust for whether or not the process used its whole timeslice);tickets[i]
to the number of tickets assigned to each process.
- setting
- Add support for a lottery scheduler to xv6, by:
- changing the scheduler in
proc.c
to use the number of tickets to randomly choose a process to run based on the number of tickets it is assigned;
- changing the scheduler in
- Submit your code by running
make submit
and submitting the result.tar.gz
file.
Some Supplied Test Programs
We have supplied some testing programs to aid you in figuring out whether your implementation is correct. Note that these programs are not complete tests. For example, they do not test that you set the correct default ticket count, that ticket counts are inherited by child processes correctly, and they may miss combinations of ticket counts some implementation techniques have problems with.
processlist.c
We have supplied processlist.c which is a test program which runs getprocessinfo
and outputs
the information in the struct.
You can add this test program the same way you added a test program for the xv6intro assignment.
timewithtickets.c
We have supplied timewithtickets.c which is a test program which:
-
takes as command line arguments an amount of time to run for followed by a list of numbers of tickets to assign to each subprocess. Each subprocess runs an infinite loop and is killed after running of the designated amount of time. By default, the process does nothing during the infinite loop, but you can change
#undef USE_YIELD
to#define USE_YIELD
to have the process instead call the system callyield()
repeatedly in the loop. -
outputs a table of the programs in order, with the number of tickets assigned to each and the number of times it was scheduled, as reported by getprocessesinfo
-
reports an error if
getprocessesinfo
-
the
num_processes
field returned is negative or exceedsNPROC
-
indicated that a child process had the wrong number of tickets;
-
was missing a child process
-
lotterytest.c
If you get an error about
__divdi3
not being defined when trying to use this, try runningsudo apt install gcc-multilib
and recompiling.
We have supplied an automated test program lotterytest.c [last updated 2020-02-03 23:55] which essentially runs several cases
of timewithtickets.c
and checks the number of times scheduled reported.
It also has a couple of test cases to make sure that programs that are not runnable don’t confuse your scheduler. Like timewithtickets.c
, it
also verifies that getprocessesinfo
returns output with correct numbers of tickets, etc.
It has some rather complicated code that attempts to perform a statistical test. We’ve set the parameters of the test so it should almost never indicate your code is wrong because of “bad luck”. However, this test might be sensitive enough to detect if you don’t use unbiased randomness (particularly if you use a pseudorandom number generator with a small range). I’m hoping this code is correct, but it’s very tricky and not easy to test if it’s too sensitive or not sensitive enough. See also the note on bias below.
Some notes on this test:
-
Many of the tests are verifying that the correct processes/ticket counts appear in
getprocessesinfo
output rather than testing your scheduler. Pay attention to which tests fail. -
When the statistical test indicates that threads were scheduled at incorrect rates, some additional tests are run to diagnose this further. Because of this, the number of tests reported in the final summary output will vary.
-
An example output when the statistical test fails is as follows:
*** TEST FAILURE: for scenario 'two processes, unequal ratio, small ticket count': distribution of schedules run passed chi-squared test for being same as expected ----------------------------------------- two processes, unequal ratio, small ticket count expected schedules ratios and observations # expect observe (description) 0 1 0 (assigned 1 tickets; running yield_forever) 1 2 33648 (assigned 2 tickets; running yield_forever) NOTE: the 'expect' values above represent the expected ratio of schedules between the processes. So, to compare them to the observations by hand, multiply each expected value by (sum of observed)/(sum of expected) -----------------------------------------
This indicates that the test ran two processes, numbered 0 and 1 in the test output. The first process was assigned 1 ticket and ran function called
yield_forever
which calledyield()
(via a system call) in a loop. The second process was assigned 2 tickets and ran the same function. The test expected a 1:2 (theexpect
column) ratio between the number of times each of these programs. Theactual
comes shows that the process 0 was scheduled 0 times and process 1 was scheduled 33648 times. If our scheduler was working properly, we’d expect something close to the 1:2 ratio, such as 11234 times and 22404 times. -
Tests will report an observed number of times scheduled of
-99999
when it fails to find a particular pid ingetprocessesinfo
’s result. -
If you get an error about processes not being scheduled enough, see hints on that issue below.
Hints
Reading on xv6’s scheduler
-
Read Chapter 5 of the xv6 book for documentation on xv6’s existing scheduler.
-
You can review Chapter 3 of the book, and/or the instructions on the intro homework on how to add system call and utility functions like
argptr
.
Reading on Lottery Scheduling
- For an alternate explanation to the lecture slides, see Chapter 9 of Arpaci-Dusseau.
Suggested order of operations
-
Implement
settickets
, but don’t actually use ticket counts for anything. -
Implement
getprocessesinfo
. Use the processlist.c or other programs (e.g. ones you write) based on it to verify that it works. -
Add tracking of the number of times a process is scheduled. Use the timewithtickets.c or other programs based on it to verify that it works. Note that with round-robin scheduling, if multiple processes are CPU-bound, then each process should run for approximately the same amount of time.
-
Implement the lottery scheduing algorithm. Use the timewithtickets.c to test it. Then use lotterytest.c to further test it.
Tracking the number of times a process is scheduled
-
proc.h
contains xv6’s process control block, to which you can add a member variable to track the number of times a process is scheduled a process has used. -
You may need to modify
fork
(inproc.c
) or related functions to make sure the times scheduled variable is initialized correctly.
Adding settickets
-
You can use
argint
to retrieve the integer argument to your system call. (Makingsys_settickets
take an argument will not work.) -
Like for tracking the number of times a process was scheduled, you will need to edit the process control block in
proc.h
. -
Follow the example of similar system calls in
sysproc.c
.
Adding getprocessesinfo
-
struct processes_info
needs to be declared somewhere accessible by both user and kernel code. One way of doing this is to declare it twice (make sure the declarations are identical), once in a header file included by kernel code (e.g.defs.h
) and once in a header file included by user code (e.g.user.h
). Another option would be to add a new header file and include it from both user header file and a kernel header file. -
You can use the
argptr
to retrieve the pointer argument in your system call handler.Note that
argptr()
takes a zero-based index of the number of the argument to retrieve, and returns whether or not retrieving the argument was successful. To actually get the value of the pointer, you should passargptr()
a pointer to the pointer. It will modify the pointed-to pointer when it is successful. -
You should iterate through the process list
ptable
, skipping over UNUSED processes. -
Look at the code for
kill
inproc.c
for an example of how to search through the list of processes bypid
. -
Before and after accessing the process table (
ptable
), you should acquireptable.lock
and afterwards you should release it. You can see an example of this inkill
inproc.c
. This will keep you from running into problems if a process is removed while you are iterating through the process table.
Adding the lottery scheduling algorithm
-
You will need to add a psuedorandom number generator to the kernel. We’ve supplied a suitable version of the Park-Miller psuedo-random number generator (use the
next_random
function to get a random number between 1 and RANDOM_MAX) from Wikipedia’s page on Lehmer random number generators. You may use psuedorandom number generator code from elsewhere, so long as you clearly cite where obtained the code from. -
It is okay if your pseudorandom number generator uses a fixed seed. But if you don’t want to do this xv6 has a function
cmostime
that reads the current time of day. -
The logic in
schedule
implements a round-robin scheduling algorithm, by iterating through all processes, scheduling each one as it iterates though them. You most likely will modify it to instead iterate through all processes (perhaps more than once) each time a new process needs to be scheduled to choose which one to run. -
xv6 does not support floating point, so you will need to do your random selection without
float
s ordouble
s. -
getprocessesinfo
provides you the information you need to test that your lottery scheduler behaves correctly. You will probably not use it in the implementation of the scheduler itself. -
When there are no runnable processes, your schduler should release the process table lock to give interrupts (like for keypresses) a chance to make programs runnable, then reacquire that lock and iterate through the process table again.
Processes not scheduled enough?
If you get errors from lotterytest.c
about not seeing processes scheduled enough, common problems include:
-
not scheduling any process when it should schedule one
-
tracking the number of times a process is scheduled incorrectly
-
scheduling processes that aren’t part of the test
-
your scheduler being very slow, perhaps due to spending a lot of time runnning added
cprintf
s. You can try to make the test wait longer by increasing the#define
forSLEEP_TIME
to see if it’s just the system being too slow.
Identifying panics
-
If xv6 prints a message containing something like
panic: acquire
, this means that something calledpanic("acquire");
. Thepanic()
function stops the OS, printing out an error message. Generally, these panics are caused by assertions in the xv6 code, checking the current state is consistent.Most
xv6
panic messages include the name of the function that called panic, so you can often search for that function name and see when it calledpanic
.In general, you can get grep the xv6 code to find out what exactly the cause was.
For example,
panic("acquire");
appears inacquire()
inspinlock.c
. It is called if a thread tries to acquire a spinlock that the current thread already holds. -
There is a panic in the
trap
function that triggers when an unexpected trap occurs in kernel mode. You can infer more about those from the table of of trap numbers intraps.h
and the message which is printed out before the panic.One of the possible traps is a page fault, which means you accessed a memory location that was invalid. This can be caused by using an invalid pointer, going out of bounds of an array, or using more space on the stack than is allocated.
Note on random bias
-
The most common source of apparent bias is off-by-one errors in using ticket counts. Looking at what process is chosen with small ticket counts (e.g. 1, 2, 3) is helpful for this.
-
The second most common source of apparent bias, especially errors with relatively large ticket counts, is something like double-counting one process’s tickets.
-
If you experience only small biases, it is possible that it is a more subtle issue with how you use random numbers. One example of this that is likely if you use a random number generator with a small range of possible outputs compared to the one we suggest:
- If you take a random number
x
in the range, say, 0 to 1023, and use it to choose a random number from 0 to 99 usingx % 100
, then you will choose numbers between 0 and 23 too often. One way to avoid this is to check ifx
is greater than 999, and, if so, select another random numberx
between 0 and 1023 (and keep doing this until you get one between 0 and 999 inclusive).
- If you take a random number
Credit
This assignment is based on Arpaci-Dusseau’s version of an xv6 lottery scheduler project.