In this section we provide background and motivation for K42 and describe its structure and system interface. One of the primary differences between K42's Linux API and Linux is that in K42 much of the standard kernel functionality has been implemented in user space. Results from the Exokernel project demonstrated that user-level implementation of operating system services can lead to significant performance gains. We have found that there are also some performance challenges (more details later, e.g., Section 3.2), especially when implementing a pre-determined model, e.g., fork. The Exokernel work showed that moving code into the application's own address space improves performance. Micro-kernel designs did not show performance improvements. K42 is more like the Exokernel.
Although performance was our primary motivation for user-level implementation of kernel services, there are other benefits. Code and data for services implemented in user space do not tie up kernel resources. User-space code can yield a cleaner programming model because issues such as stack violations are easier to protect against. In user space, allocating virtual stacks, versus pinned stacks in the kernel, allows quasi-infinite stacks with red pages guarding the end. Moving code into the application's own address space does not introduce protection issues. The application still only has access to resources that the prior Linux permissions would have allowed. Moving code into user-level servers introduces a potential scheduling challenge that we address by running the servers at a higher priority than applications.
K42 has addressed security in a first-class manner. All IPC calls between applications, servers, and the kernel contain a badge that securely identifies the caller and is guaranteed by the kernel. For each object invocation on the callee side there is a series of matched rights the badge is compared with to ensure the caller has the proper authentication to make the call. The infrastructure in K42 allows for different security models between different servers and different applications as desired.
In K42, all thread scheduling is done by a user-level scheduler and requests that would normally block in the kernel, e.g., page fault waiting for disk I/O, do not block in the kernel but are instead returned, with control, to the user-level scheduler. The user-level scheduler can then block the thread and continue running another thread in the same address space. This positively affects the handling of signals, asynchronous I/O, sockets, and other aspects of providing the API.
Other salient aspects of K42's design impacting the implementation of a Linux API are the pervasive use of object-oriented technology and our locking strategy of both avoiding global locks and of not holding locks while accessing multiple objects. The former locking strategy implies a pervasive methodology for fine-grained locking (i.e., no global kernel lock as in Linux). The latter implies that locks may not be held for the entire duration of performing a task, such as a fork-chain collapse (reducing the length of the tree representing forked processes), implying that in-flight requests must be algorithmically accounted for, i.e., if locks are not held across object invocations then multiple requests may occur simultaneously and need to be accounted for.
In the rest of this section we describe K42's goals and motivations, its overall structure, and the key technologies used in its design and implementation.