0019: CW4 Graphical Memory Maps

This web page shows color, animated versions of the memory maps in the 0019 CW4 handout, along with their accompanying text.

Getting Familiar with WeensyOS Memory Maps

Once you've cloned your repository from GitHub to your working environment, you can build the initial version of WeensyOS we've given you by issuing the shell command make run in your CW4 directory.

You should see something like the below, which shows four processes running in parallel, each running a version of the program in p-allocator:

The animated image above loops forever; in an actual run, the bars will move to the right and stay there. Don't worry if your image has different numbers of K's or otherwise has different details.

If your bars run painfully slowly, edit the p-allocator.c source file and reduce the ALLOC_SLOWDOWN constant.

Stop now to read and understand p-allocator.c.

Here’s how to interpret the memory map display:

Here are two labeled memory maps showing what the characters mean and how memory is arranged.

The virtual memory display is similar:

The Five Stages of WeensyOS

We describe below the five implementation stages you must complete in CW4: what you need to implement in each, and hints on how to do so.

Stage 1: Kernel Isolation

In the starting code we've given you, WeensyOS processes could stomp all over the kernel's memory if they wanted to. Better prevent that. Change kernel(), the kernel initialization function, so that kernel memory is inaccessible to applications, except for the memory holding the CGA console (the single page at (uintptr_t) console == 0xB8000.) Making the console accessible in this way, by making the range of RAM where the contents of the display are held directly accessible to applications, is a throwback to the days of DOS, whose applications typically generated console output in precisely this way. DOS couldn't run more than one application at once, so there wasn't any risk of multiple concurrent applications clobbering one another's display writes to the same screen locations. We borrow this primitive console design to keep WeensyOS simple and compact.

When you are done, WeensyOS should look like the below. In the virtual map, kernel memory is no longer reverse-video, since the user can't access it. Note the lonely CGA console memory block.


Stage 2: Isolated Address Spaces for Processes

Implement process isolation by giving each process its own independent page table. Your OS memory map should look like this when you're done:

That is, each process only has permission to access its own pages. You can tell this because only its own pages are shown in reverse video.

What goes in per-process page tables:

The reverse video shows that this OS also implements process isolation correctly.

How to implement per-process page tables:

If you create an incorrect page table, WeensyOS might crazily reboot. Don't panic! Add log_printf() statements. Another useful technique that may at first seem counterintuitive: add infinite loops to your kernel to track down exactly where a fault occurs. (If the OS hangs without crashing once you've added an infinite loop, then the crash you're debugging must occur at a point in the kernel's execution after your infinite loop's place in the code.)

Stage 3: Virtual Page Allocation

Thus far in CW4, WeensyOS processes have used physical page allocation: the page with physical address X is used to satisfy the sys_page_alloc(X) allocation request for virtual address X. This strategy is inflexible and limits utilization. Change the implementation of the INT_SYS_PAGE_ALLOC system call so that it can use any free physical page to satisfy a sys_page_alloc(X) request.

Your new INT_SYS_PAGE_ALLOC code must perform the following tasks:

Don't modify the physical_page_alloc() helper function, which is also used by the program loader. You can write a new function if you need to.

Here's how our OS looks after this stage:

Stage 4: Overlapping Virtual Address Spaces

Now the processes are isolated, which is excellent. But they're still not taking full advantage of virtual memory. Isolated address spaces can use the same virtual addresses for different physical memory. There's no need to keep the four processes' address spaces disjoint.

In this stage, change each process's stack to start from address 0x300000 == MEMSIZE_VIRTUAL. Now the processes have enough heap room to use up all of physical memory! Here's how the memory map will look after you've done it successfully:

If there's no physical memory available, sys_page_alloc() should return an error to the caller (by returning -1). Our model solution additionally prints "Out of physical memory!" to the console when this happens; you don't need to.

Stage 5: Fork

The fork() system call is one of Unix's great ideas. It starts a new process as a "copy" of an existing one. The fork() system call appears to return twice, once to each process. To the child process, it returns 0. To the parent process, it returns the child's process ID.

Run WeensyOS with make run or make run-console. At any time, press the "f" key. This will soft-reboot WeensyOS and cause it to run a single process from the p-fork application, rather than the gang of allocator processes. You should see something like this in the memory map:

That's because you haven't implemented fork() yet.

How to implement fork():

When you're done, you should see something like the below after pressing "f":

An image like the below, however, means you forgot to copy the data for some pages, so the processes are actually "sharing" stack and/or data pages when they should not: