The K42 project is developing a new open-source operating system kernel incorporating innovative mechanisms and policies and modern programming technologies. Our goal is to start from a ``clean slate'' and examine the system structure needed to achieve excellent performance in a scalable, maintainable, and extensible system. Although we wanted to design from scratch, we could not, nor did we want to, implement all aspects of an operating system from scratch. Further, requiring applications to use a new API would make experimenting with the system unpalatable to potential users. We therefore did not introduce a new personality, but instead made K42 Linux API- and ABI-compatible. This paper examines what we needed to do to achieve this compatibility.
K42 has a set of design features that made implementing this compatibility an interesting task. Specifically, in keeping with the design strategy of K42, the API was implemented mostly in user space, had to interface with K42 object-oriented technology, could not use any global locks, and could not hold a lock across multiple object calls. As in many areas of system design, these features both simplified and complicated the implementation. Other scalable and maintainable systems exhibit similar characteristics. Throughout the paper we point out experiences where K42's features impacted (both positively and negatively) the implementation of the API.
To make this approach of supporting the Linux application environment effective, K42 needs to fully support the Linux API and ABI. There is no porting to K42. Any application that runs on Linux just runs if using K42's ABI support and just needs to be re-compiled to use K42's API support. Most applications, including significant benchmarks, run without recompilation. If the application does not run, we fix K42 until it does. We will describe K42 in more detail later, but currently K42 can run significant Linux applications. For example, we have run the SPEC SDET benchmark suite, an Apache web server, and the full ASCI Nuclear Transport Code.
Our model for mapping a Linux environment onto K42 is illustrated in Figure 1. A Linux Application Environment, as defined by any of the popular distributions, consists of several layers and modes of operation and execution. As with most Unix-like operating systems, Linux can be segmented into a user level and a kernel level. The user level is the portion of Linux that interacts directly with the user processes, employing the services offered by the kernel level. The kernel level presents the image of a single system to each user as well as each application run by that user. It is responsible for managing all resources and securely sharing them.
In order to provide a complete Linux application environment, K42 must provide a set of interfaces that deliver all system services. These come in the form of library functions, system calls, user commands, special or device files, and file formats. Though some of these services do not require kernel support, many of them expose functionality exported by the kernel.
The focus of this paper is on how we provide a Linux application environment on top of K42, a kernel designed for scalability and extensibility. As noted, there are many services that need to be provided in order to present the user with a full application environment. In this paper we examine the major categories of these services and describe how we implement them. In particular, we describe how we emulate the Linux process tree; emulate fork, exec, and clone; support glibc and translate its system calls, provide file systems and sockets, and implement dynamic linking via ld.so.
In addition to supporting a full user environment, we wanted to be able to use the existing Linux-kernel code base to provide the desired range of hardware drivers in K42. Although this paper focuses on the user application environment, we briefly outline our kernel strategy here (more details can be found in Auslander et. al.). We use the Linux code base for hardware driver support, for networking and file-system code, and to provide a stable inter-operable environment. To be able to use Linux device-driver, networking, and file-system code we needed to provide a Linux-kernel environment (or Linux emulation environment, as in Goel and Duchamp). To do so, K42 presents itself as a target hardware architecture for Linux (in the same way as real hardware architectures such as Alpha, i386, and PowerPC do). This requires implementing the basic functionality required of architecture-specific code in Linux (e.g., assembly-level constructs, definition of locking mechanisms) in an ``emulation layer'' that can be linked with the individual Linux-kernel components to be used in K42. For example, device-driver code uses locks; these locks need to be mapped onto K42 locks and thus the Linux device-driver code needs to be compiled to run in the K42 environment. Further, K42 emulates the Linux-kernel services (e.g., kernel memory allocation) needed to support these components. As seen in Figure 1, this means that K42 replaces the core memory management, process management, and device management code of Linux, but uses file systems, device driver, and library (e.g., glibc) code from Linux.
The rest of this paper is organized as follows. Section 2 starts by introducing K42, describing its motivation and structure, and presenting its system interface. Although much of the API implementation is interrelated, we divided it into mechanisms external to the process, presented in Section 3, and internal to the process, presented in Section 4. Throughout these sections we present the advantages gained from K42's infrastructure as well as the complications that arose. In Section 5 we describe the status of K42, provide a performance evaluation of K42 and Linux running the SPEC SDET benchmark, and present work related to K42. Concluding remarks are presented in Section 6.