11/1 Kernel4: Building kmalloc -- the kernel's malloc implementation
You may have noticed that you were not able to call
malloc in Weensy OS. Most real kernels provide a kernel level implementation of
kfree. We're going to help you build a simple implementation of these routines for Weensy. For now, you may assume that you do not need to support allocations larger than a page size (4096). In fact, you can assume that you do not need to support allocations larger than a page size minus some overhead. (If you're not sure what we mean by overhead, think about what you used to manage malloc'd memory in assignment 1 -- you will undoubtedly want some kind of similar structure here.)
In the cs61-exercises repository, you'll find a version of Weensy in kernel4x. The file
k-malloc.c contains some test harness code and skeleton functions for
kfree. We've set up a structure so that you can write your
malloc implementation at user level (directly on the appliance, not on Weensy) and test it there. Once that is working, in theory, you should be able to take the same code and run it inside of Weensy! If you try to do that now (e.g.,
make run), you'll see a panic telling you that malloc failed -- that isn't too surprising since you haven't implemented it yet.
At the top of the
k-malloc.c file, you'll see some code surrounded by
#ifdef STANDALONE -- this is what lets you run either at user-level or in Weensy. Do not change anything between the
#ifdef and the
#endif. Your implementation should be expressed in terms of the macros GETPAGE and FREEPAGE which will get you a chunk of memory of size 4096 bytes. In the user-implementation, we simply call
malloc. In the Weensy implementation, we obtain an unused page from the operationg system. You job is to use those blocks relatively efficiently to satisfy the much smaller requests that will be made to malloc.
If you prefer to do this without further hints, go for it now! Use the script
testkmalloc.sh to build and test your user-level implementation. (We use
valgrind to check that your implementation frees everything it allocates.)
If you're prefer some hints:
1. You'll probably want a static variable or two to keep track of blocks that you've gotten from
malloc or the operating system. Additionally, you'll undoubtedly want to design some meta-data structure that you will use to keep track of the space that you've allocated. (Think Problem Set 1).
2. Next, you might want to get
kfree working as simply as possible: regardless of the size requested, return the entire page that you get from
malloc or the operating system. If you implement both
kfree this way, you should be able to run both at user-level and in Weesy, except you'll run out of memory in Weensy -- feel free to change NALLOCS for now, so you can test it. (Don't forget to verify that the caller did not ask for more memory than you can reasonably give out -- after all, you're programming in the kernel now -- you should be defensive!)
3. You might claim that you are now done, but this is a terribly inefficient implementation! Let's get smarter. Modify your implementation so that if the user asks for only a few bytes, you return a small number of bytes (the amount requested plus some overhead). This will undoubtedly require changes in both
kfree. Don't worry too much about using every scrap of memory -- get something working!
4. Now you can start to improve your implementation in a couple of ways. You may do some or all of the following:
- At the end of the
testmalloc, return all the pages that you ever allocated.
- Coalesce memory blocks when you free them. That is, let's say that you allocate chunks A, B, and C contiguously from within a page and then free the chunks in the order A, C, B -- it would be great if a new request for something the size of A+B+C could be satisfied using the same memory.
5. Finally, if you've got an implementation that you like, see if you can modify Weensy to dynamically allocate process structures rather than using a statically allocated, fixed size array!