CS170: Lab 0 - Helpful Hints from your Aunt Heloise

Occasionally, your Aunt Heloise (who has served up many a fine batch of operating systems delight) will provide you with some of her insights, tips, and cooking secrets so your your CS170 assignments can be as tasty as you want them to be. For this lab, the cooking temperature that you use, and the order in which you add your ingredients, makes a huge difference. Cook too fast, and you'll burn it -- too slow, and your TAs will grow hungry waiting for your submission. Here is how your Aunt Heloise whipped up her latest batch of malloc(). You can do it too.

Start with InitMyMalloc()

The key to this lab (and most of the other labs) is not to try and code everything at the beginning. Yes -- I know. The strategy of writing everything first and then debugging might lead you to believe that you will get more partial credit since you can always claim it was "almost working." Unfortunately, your Aunt Heloise doesn't quite buy this argument in the same way that the TAs won't buy it, your future boss won't buy it, and your co-workers won't buy it when you are late with your part of a joint project. In this class, you will be much better off with working code than with a file full of C programming that you've crammed in at the last minute.

Therefore, your best strategy is to try and get the program working in stages. To do so, you are going to want to write modules and test routines at the same time. Each time you add a feature you should also write a function to test that feature. The program testmymalloc.c was written in this way. Each test went in to exercise a different part of the code.

The first routine to write, then, is InitMyMalloc() and the second one you should write is PrintMyMallocFreeList(). As mentioned in the lab write-up you will need to define a data structure to describe each block you allocate and free. You can use what ever data structure you like, but your Aunt thinks that

struct malloc_stc
{
        struct malloc_stc *next;
        struct malloc_stc *prev;
        int size;
        unsigned char *buffer;
};
is like a good diamond: simple, elegant, goes with everything, and valuable.

The buffer you will use for space must be declared as a global variable. You will also probably want to declare a free list head pointer as a global variable at the same time. Your Aunt declared them as


static struct malloc_stc *MyFreeList;
static unsigned char MyBuff[MAX_MALLOC_SIZE];

The function InitMyMalloc() must do two things. It must To make this go, you will need to do a little casting. Here is a code segment that surely constitutes the biggest hint provided in the history of Western Civilization.

MyFreeList = (struct malloc_stc *)MyBuff;
MyFreeList->next = NULL;
MyFreeList->prev = NULL;
MyFreeList->size = sizeof(MyBuff) - sizeof(struct malloc_stc);
MyFreeList->buffer = (unsigned char *)(MyBuff + sizeof(struct malloc_stc));

This is the code that makes up the body of your Aunt's InitMyMalloc().

Stop.

Before you do the cut and paste thing, take a deep breath and relax. The code isn't going anywhere. Take a moment to consider this code carefully. It isn't many lines, but you should not proceed unless you understand exactly what each line does and why. If you don't see why each assignment is made in the way it is, you will not have much hope of completing the rest of the assignment. Draw yourself some pictures. Why is MyFreeList->size set this way? You might wonder why the field buffer is in the structure. It points to the first byte of storage in the buffer usable by the caller of MyMalloc(). Why is it set this way? What is going on with the casts?

If you don't understand these basics, what can you do? Certainly, you can ask the TAs but that solution will probably not get you much past a working version of InitMyMalloc() (which won't count for any points since it is pretty much all here). Understanding how and why InitMyMalloc() is written this way requires that you have a fair understanding of C programming. If you do not, you should plan on brushing up on your C before you proceed. Your aunt notes that your instructor has provided some lecture notes on C to help reacquaint you with its subtle flavors and textures. In addition, there are many courses on-line, and hundreds, if not thousands of books available. C will be absolutely essential for all of the assignments in this class. No matter how good your Java or C++ skills are, for this class you will need to know C at this level.

PrintMyFreeList()

Your next step is to write a print function that can print out all of the free blocks in your free list. This function should be very simple to write. The only thing you need to know is that the end of your doubly-linked list will be signified by a NULL pointer. Yes -- your free list should be doubly-linked.

Test It

Before going any further, you should write a test function. You can write it from scratch. Use the code for simpletest1.c as an example, if you like. Add your test routine to the makefile so that it builds properly. If you don't understand how a makefile works, it is time to do a little bit more study on your own. All reasonable systems use the make utility to manage code construction. Your test code should call InitMyMalloc() and PrintMyMalloc() and then exit. Build it, and run it. Is the output what you expected?

Start working on MyMalloc()

Your next step should be to start writing the function MyMalloc(). Given the configuration of your free list after InitMyMalloc() has been called, what are you going to have to do when a call to MyMalloc() occurs? Read the lab write-up and look at the discussion of splitting. Write only the part that splits the first buffer in the list. Write a new test program and test only this function. Use your PrintMyMalloc() to make sure your free list looks right after a call to MyMalloc(). You should also test the case when a call to MyMalloc() cannot be satisfied because the request is too large. Be sure that all of your sizes are rounded up to the nearest multiple of 8.

Start on MyFree()

Once you are confident that you have this part of MyMalloc() working, you will need to start on MyFree(). Don't worry about coalescing at this point. Just make sure that a free block returned to you via a call from is added in the right place on your free list. There are two tricky things here. First, the pointer passed to you in MyFree() is not a pointer to your struct malloc_stc but rather the pointer you handed back from a successful call to MyMalloc(). If you use your Aunt's data structure, that pointer is the pointer contained in the field buffer. You will need to recover the pointer to the structure from this pointer. Think carefully and look at how InitMyMalloc() works. If you understand that routine, it is trivial. Next, you need to make sure that your free list remains sorted in terms of the block addresses. Each call to MyFree() then must find the right spot in the list (this is technically an insertion sort).

Test It

Write yourself another test program and make sure you can free the space you allocate. Use your print routine to make sure that the data structures look right.

Test it Some More

At this point, you have written the code to allocate and then free the first block. What if you do two allocates and then free the second? That should put two blocks on your free list that are "next" to each other in your MyBuff array. Write a test code to test this case.

Go Back to MyMalloc and do First Fit

Now, you have a way to make a free list with multiple blocks, but your version of MyMalloc() doesn't yet implement "first fit." Change it to look for the first block on the free list that is big enough to hold the requested size. Make sure that you take care of two cases. The first case is when you need to split the free block because there is some left over. The second case is when the fit is "tight enough" so that there is not enough left over to hold your bookkeeping structures.

Test It

Write several more test codes. At this point you should have everything working except coalescing. Make sure that you handle the failure cases correctly too, especially with respect to fragmentation. Feel free to use the programs simpletest1.c and simpletest2.c directly and as the basis for your own test codes. Also, your Aunt has developed a particularly tasty test code specifically for the non-coalescing case. It can be found in testnocompact.c. Try this one as well.

Coalescing

Now you should write the coalescing code. Write and test it incrementally in the same way you did the other codes. Coalesce one block -- then two. Make sure you can coalesce the entire list. The easiest way to make sure you are testing correctly is to do your coalescing just before MyFree() exits. In addition to your test codes, try your Aunt Heloise's version of testmymalloc.c and see if it works too.

And that's it. You should now have a firm grasp of how malloc() works. Ask yourself some questions. What happens if you malloc() a buffer that is 30 bytes long and then write 40 bytes into it? What happens to the extra ten bytes? What happens if you forget to free memory?