In this lab, you'll write a simple non-preemptive thread package.
We'll have to save and restore registers for thread context switching and know how procedure calls work at the assembly level:
6-threads/docs/subroutines.hohl-arm-asm.pdf
has a reasonable overview of both. It's written for assembly-programmers so you should skim to where there are annotated places.- For ARM assembly code, the ARM asm talk in the
6-threads/doc
directory is a reasonable crash course. - The ARM manuals in our top-level
docs
directory have the definitive, detailed description of anything you need to know.
Some useful introduction (or review) reading for threads:
- Lecture 2 and Lecture 3 from Mazieres' CS140 lectures.
- Threads intro and Thread API from the useful Three Easy Pieces textbook.
Deliverables:
- Implement kernel memory allocation in
1-kmalloc
(see below). - Look through the
2-threads/rpi-thread.[ch]
header and skeleton code so you see what you will be implementing. - Read about threads carefully, especially the API chapter above.
- Figure out these puzzles: what do we have to do to context
switch from one thread to another? How to switch from "normal
execution" to the first thread? How to handle the case where a
thread running
foo()
does not callrpi_thread_exit
explicitly and simply returns back to its (non-existant) caller?
In today's lab we want to create an arbitrary number of threads and
stacks, so we would like a kernel memory allocator. To let us focus on
threads we are are just going to do a trivial one that does not support
free's of individual blocks. The great thing about memory allocation
without free is that it is trivial: simply increment a pointer! (Note,
we do have a free of the entire memory space: it's called rpi_reboot
).
The first problem we have is figuring out where the heap starts. We will use a simple hack: just put it at the end of the pi program:
- If you look at the linker script
libpi/memmap
, you can see that it tells the linker to place a symbol__heap_start__
at the end of the executable so we can locate it. The address of this variable is the address of the start of the heap.
The second problem we have is that when we allocate, we have to make sure that the memory returned can be accessed without causing an alignment fault. Since the pi only requires 4-byte alignment, we can just make sure every returned block is at least 4-byte aligned (i.e., the lower two bits should be 0).
Your implementation should do the following:
-
In
kmalloc_init
set the heap to be the address__heap_start__
is located at. Make sure it is 4-byte aligned. Print it to make sure the address makes sense. -
In
kmalloc(nbytes)
roundupnbytes
to be a multiple of four (use theroundup
macro) and increment the heap pointer by the result so that the next allocation occurs afterwards. Assert that the result makes sense.
When you are done:
- Compile and run
test-kmalloc
; feel free to add a bunch of tests.
I did a bare minimum. It checks that you roundup alignment and allocate no more than is needed. - If it passes: . Copy your implementation to
libpi/my-src
. - Check that
2-threads
(which useskmalloc
) compiles without a link error. If you get one you probably didn't copy intolibpi/my-src
.
Various further readings:
- A reasonable article on linker scripts
- [Regon allocation] (https://en.wikipedia.org/wiki/Region-based_memory_management).
- [Arena allocation] (http://drhanson.s3.amazonaws.com/storage/documents/fastalloc.pdf).