JOS Virtual Memory (part 1)

Introduction

In my last post I mentioned I was writing about JOS’ virtual memory implementation. At first, I was going to write only one post but I have written so much that I had to split the post into two parts.

The first (this one), will talk about the exokernel design and will have a first look at JOS’ memory management interface with user-space.

The second post will explain how the system calls presented in the first post work. But, instead of a boring explanation, I will show how the system calls fit together to implement a real JOS’ feature: fork() with copy-on-write support. This feature is impressive because it is done mostly in user-space.

For both posts some background in memory management and operating systems is needed. For those who have learned it but forgot the details, the wikipedia article on virtual memory seems to be sufficient. If you have never studied this subject before, I recommend you to read the memory management chapter from some operating systems book (I have learned from Tanenbaum’s).

Something important to bear in mind is that I am just a beginner on this subject. Everything I know I have learned while working on JOS, so it is likely that you will find mistakes in this text. If you do, please, let me know by sending me an email.

Quick introduction to exokernel design

The important idea behind the exokernel design is that its interface with user-space is as simple as possible. In fact, what an exokernel does is to securely export all hardware resources to user-space and give applications the freedom to manage the hardware resources for themselves.

Most of what a Unix user would recognize as being an operating system task is implemented as an application library in an exokernel.

For example, in Unix kernels the basic system call to create a new process is fork(). The application calls fork() and a new process is created. Everything is done in kernel-space and, roughly speaking, the application has no control over the process.

In an exokernel design, though, fork() is implemented in user-space. Here the application has full control over the process and could even define its own fork() with different semantics. Actually, fork() is not even special because it is just an ordinary library function.

Additionally, most kernel services that are usually implemented in kernel-space (such as File-systems and Networking) can be implemented by user-space applications in an exokernel design.

The main advantages of this design are:

1. The kernel does not limit the freedom of the application to design its own abstractions for physical resources
2. In some cases, due to the high costs of heavy abstractions in kernel-space, you can have performance benefits from implementing abstractions in user-space
3. Exokernels are small

Of course, there are disadvantages, one of them being that exokernel applications may not be portable among different exokernel implementations.

For more information regarding the exokernel design, I recommend to read the original exokernel’s paper.

A First look at JOS’ memory management interface

The following system calls are available to user-space applications:

int sys_page_alloc(envid_t envid, void *va, int perm);
int sys_page_map(envid_t srcenvid, void *srcva, envid_t dstenvid, void *dstva, int perm);
int sys_page_unmap(envid_t envid, void *va);
int sys_env_set_pgfault_upcall(envid_t envid, void *func);

Before going into the details, it is important to understand what an environment is. In exokernels, an environment is the same entity as a process in Unix. Environments also have identification numbers, referred to as ‘envid’.

I will now describe in detail what each of the system calls above do. You may have the feeling that things are not very clear. Do not worry, things will get clearer soon:

sys_page_alloc(): allocates a page of physical memory from the free list maintained by the kernel and inserts that page into the address space of the environment at address ‘va’. The page permissions is set to perm.

sys_page_map(): maps the page of memory at ’srcva’ of environment ’srcenvid’ into address ‘dstva’ of environment ‘destva’. The page permissions are set to ‘perm’.

sys_page_unmap(): unmap the page of memory at ‘va’ in the address space of ‘envid’. If this is the lasting mapping of the page, it’s inserted back to the free list.

sys_env_set_pgfault_upcall(): set the environment ‘envid’ page fault handler. When a page fault happens, the kernel branches to ‘func’.

Basically, to allocate memory in JOS, an application has to call sys_page_alloc(). It means that it has to know where the physical page should be mapped in its address space.

Of course, for widely used features (like memory allocation), the application could (and should) implement a memory allocation function, say malloc(). Such a function would define a specific area in the address space of the application for memory allocations, and manage it.

Indeed, JOS has malloc() implemented as a library function and it works as just explained. But it is important to note that it is more like a convention, because the application is still free to implement its own memory allocation function with whatever features it needs.

The next post will explain how the system calls presented above work in a real life JOS feature: fork() with copy-on-write support.

One Response to “JOS Virtual Memory (part 1)”

  1. Luiz on Nothing » Blog Archive » JOS Virtual Memory (part 2) Says:

    [...] Luiz on Nothing « JOS Virtual Memory (part 1) [...]