Friedrich-Alexander-Universität Erlangen-Nürnberg  /   Technische Fakultät  /   Department Informatik
Assignment 5: Fork & Map

In Unix-like operating systems, fork is used by a process to create a new child process. The child process is a duplicate of its parent, but runs in a different address space, with its own context.

Videos (in German)

Process Management System Calls

You have to implement the following system calls (which are quite similar to their Unix equivalents):

  • int fork();
    Create a new process duplicating the calling process, including the process state: On activation, the new child process shall start at the same point its parent will resume. The only difference is the return value of the call – the parent will get the process ID (PID) of its child, while the child will receive 0.
  • int getpid();
    Retrieve the process ID of the calling process. The PID has to be a unique thread identifier in the whole system.
  • int getppid();
    Retrieve the parent process ID of the calling process. If it was not created by fork, it will return 0.

Implementation Details

All page frames belonging to the calling process have to be copied in fork. The fork system call's stub in user space is allowed to contain additional code to save and restore the contents of the non-scratch registers. This way you have the ability to re-use your existing infrastructure for launching a user space thread.

Extended Memory Management (7.5 ECTS)

User space applications sometimes require dynamic memory allocation. Extend your kernel to provide new system calls to fulfill this demand:

  • void* map(void* addr, size_t size);
    Zeroed user memory with at least enough space for size bytes is mapped into the calling threads address space at addr. In case the requested address is NULL, the kernel has to find a suitable place in the user space. On successful execution, the system call will return the allocated address. If allocation is not possible (or not allowed) it shall return NULL.
  • void exit();
    This call will quit the current thread and free all resources associated with it. Under no circumstances is this system call allowed to return to the calling thread.

Validate the correctness of your system call implementations using a suitable test program: The exit system call, for example, can be tested by reporting the current state of the page frame allocator before loading a thread and after exit() – the number of unused pages should be the same.

Dynamic Allocator for User Space

Once the map system call works correctly, it is quite easy to port the buddy allocator from the kernel to your user space library (libsys). The following example allows a dynamic allocation of up to 64 MiB in each application (as long as there is enough continuous memory mappable):

void * malloc(size_t size) {
return reinterpret_cast<void*>(allocator.malloc(size));
}
void free(void *ptr) {
allocator.free(reinterpret_cast<uintptr_t>(ptr));
}

This in turn allows not only the usage of additional functionality like strdup but also implementing operator new and operator delete in the init.cc to dynamically create objects in user space.

uintptr_t malloc(size_t request)
Allocate uninitialized memory.
Definition: alloc_buddy.h:338
void free(void *ptr)
Free an allocated memory block.
Definition: alloc.cc:59
void free(uintptr_t ptr)
Free allocated memory.
Definition: alloc_buddy.h:444
void exit()
Deinitialize this CPU core.
Definition: core.cc:53
void * malloc(size_t size)
Allocate a memory block The memory is not initialized.
Definition: alloc.cc:49
Buddy Allocator Template.
Definition: alloc_buddy.h:67