Introduction

This survey covers the three following papers:

  1. Rethinking the library OS from the top down;

  2. Exokernel: An operating system architecture for application-level resource management; and

  3. Mach: A new kernel foundation for UNIX development.

Operating systems serve as an interface between applications and hardware. They hide away the details of the hardware by providing basic abstractions, and virtualize convenient objects for the use of applications. However, traditional operating systems are, by design, monolithic and general-purpose. This limits their performance as they have to compromise and take up policies that perform well in generic settings.

Virtual operating systems provide customized platforms for running software applications. They provide a level of abstraction between the application and the hardware, meaning applications that are developed for a certain operating system can run on the virtual OS regardless of the underlying hardware. They can also be used to run legacy software, meaning one can run an old and outdated virtual operating system without having to maintain old physical hardware.

With the rise of cloud computing, virtual operating systems have become more and more popular. On a cloud server, multiple users would run different applications in different environments. Therefore, virtualization becomes a necessity. Virtual operating systems allow for the allocation and management of the resources between different applications. They also, provide isolation and security which is crucial to prevent the different applications to corrupt each other or the host.

The papers [1] and [2] take the library operating system approach to virtualization, where the personality of the operating system is captured in a library, i.e. the library OS. The library OS then communicates with the kernel through a well-defined interface. [3] proposes an architecture where the personality is included in user-level services. Each of these approaches for creating new operating system personalities, can be used to tailor the operating system to specific circumstances, thereby maximizing the performance of applications.

Overview of the Papers

In [1], the authors introduce a top-down approach to refactorizing monolithic operating systems and extracting the operating system personality. They discuss techniques that allow lightweight virtualization and the creation of a secure, portable, and fast library OS. By getting rid of the overhead, they provide a more efficient alternative to the traditional heavy-weight virtual machines. As an illustration, they use their approaches to develop a library OS called Drawbridge from Windows 7, and perform some benchmark tests to evaluate Drawbridge's efficiency.

In [2], the authors discuss a minimal OS kernel architecture called exokernel. An exokernel serves as a thin layer over the hardware and exposes all the resources directly to user-level applications. This approach reduces overhead by eliminating unnecessary layers of abstraction between the hardware and the application. It also creates an extremely customizable environment for the applications. This paper substantially differs from [3] in that it provides very little separation between the application and the hardware.

In [3], the authors describe the microkernel Mach which provides great flexibility. Mach separates the operating system into a small, kernel-level component and a larger set of user-level servers that provide the necessary services. It achieves modularity through the object-oriented design of the servers, and a port-based communication system between them. Mach allows for greater customization of the operating system's behavior.

Survey Organization

The following section of this survey is devoted to the common themes that appear in the papers. In the subsequent three sections, we delve deeper into the details of each paper. Finally, in the last section, we address some of the shortcomings of the approaches discussed in the papers.

Recurring Themes

In this section, we will explore the common themes that appear in the three papers. Although the papers take somewhat different approaches to creating new operating system personalities, their approaches share many characteristics that seem intrinsic to this task. These key characteristics help achieve many goals such as higher performance, more security, or more flexibility.

Overall, the three papers advocate for minimal operating systems that minimize overhead, expose low-level resources, and are highly modular, allowing for greater flexibility and customization.

Customizability: The key point of all three approaches is customizability and flexibility. The papers propose different approaches for efficiently creating new operating system personalities. As a result, the operating system can be specifically tweaked for the needs of any application. This would considerably improve the performance of the application.

Minimality: All three papers pay attention to minimality. By keeping as little implementation in the kernel as possible, one can reduce the overhead to any system calls, while providing more room for customization in the library OS.

Modularity: All three papers promote modularity. By breaking down complex functionality into smaller components, one can create logical boundaries between different parts of the system. As a result, modularity makes for easier maintenance of the OS and development of applications. The isolation of the components allows each of them to change internally or to be entirely replaced with minimal disruption. It also helps with security.

Application-focused design: All three papers employ application-focused designs. They consciously give applications more control over the resources and more access to the hardware. This is in contrast to traditional operating systems, where the resources are managed centrally in the kernel.

A Top-Down Approach Towards Building Library OS

Library OS can be considered a lightweight alternative to traditional virtual machines. They provide the same security and independence through the isolation of applications. In the traditional virtual machine approach, multiple VMs are created, each with its own operating system instance. The result is a strong isolation at the cost of high overheads. In comparison, a library OS gives applications direct access to system services through linked libraries, resulting in much less overhead.

Through benchmark comparisons, the authors show that overall OS libraries have better performance than traditional VMs. In terms of memory, each library OS only needs its own private kernel library whereas each VM would incur the cost of storing the whole guest OS. In terms of system calls, OS libraries have order-of-magnitude lower overheads because of a thinner virtualization layer. In terms of portability, a library OS has much a smaller snapshot. So the use of library OS would be beneficial for the migration of running applications between computers.

The Top-Down Approach

[1] discusses a top-down approach to building library operating systems. The key idea is to move low-level services to a host OS. Then, the library OS can communicate with hardware services in the host OS through a narrow application binary interface (ABI), and the OS personality would be contained in the library OS. This approach prioritizes application compatibility, and allows the library OS to avoid the management of low-level hardware services.

Contrary to other library OS designs, such as exokernel, this top-down approach aims to free the application level from the need to directly manage the hardware. To do so, it provides high-level abstractions that make it easier to share the host OS resources. By decoupling the libraries from the hardware, this approach provides a more modular and flexible design for operating systems.

To refactor the OS, the authors divide the OS implementation into three categories: hardware services, user services, and application services. Then, they move the application services to the library OS, and contain the hardware services and user services in the host OS. This allows for the independent rapid evolution of the library OS and the kernel.

Drawbridge

As a guiding example, the authors of [1] create a library OS version of Windows 7 called Drawbridge. They show that Drawbridge can run major applications. Their benchmarks demonstrate that, compared to virtual machines, Drawbridge can perform many operations at a fraction of the overheads.

As a first step, the authors extract the Win32 personality from the rest of Windows 7 to create a library OS. They accomplish this by using four heuristics: inclusion of API DLLs based on their usage in a representative set of applications; reuse of virtualized host OS resources; resolution of dependencies through inclusion or alternative implementations; and device driver emulation. They also recognize that a large part of the code in Windows 7 is related to sharing resources between multiple applications or users. Therefore, that part of the code could be removed when creating the library OS.

As the other part, they create the interface through which the library OS communicates with the host OS, which they call Drawbridge ABI. This interface consists of a small set of well-defined functions, so it can be supported by a wide range of host OS implementations. It provides high-level abstractions commonly used by many operating systems; such as virtual memory, multi-threading, I/O mechanisms, process creation, etc.

Exokernel: a Minimal Kernel for Exposing the Hardware

[2] studies another operating system architecture called exokernel. It aims to improve application performance by granting direct access to hardware resources at the application level. To do so, an exokernel separates the management and the protection of the resources. This approach is motivated by the following commonly held belief: That the lower the level of a primitive, the more efficiently it can be implemented, and the more freedom it grants to implementors of higher-level abstractions.

Fixed high-level abstractions limit the functionality of applications. The abstractions act as a barrier between the applications and the hardware resources, and all applications must use the same set of abstractions. This lack of flexibility restricts the ability of applications to interact with the hardware resources in a manner that is optimized for their specific needs. Also, the fixed abstractions tend to change rarely, if ever. The authors suggest that this is the reason why many novel ideas have not been implemented in operating systems.

An exokernel allows abstractions that are traditionally implemented in the kernel to be implemented at the application level by untrusted software. This lifts the burden caused by high-level abstractions and greatly enhances performance. For example, the operating system no longer has to make trade-offs between sparse or dense address spaces, read-intensive or write-intensive workloads, etc.

Protecting the Resources

An exokernel must include policies to arbitrate between competing library operating systems. This is not unlike traditional monolithic operating systems that control the resource allocation to different applications. An exokernel can enforce partitioning strategies such as quotas or reservation schemes by deciding which allocation requests to grant and which to revoke. Thus, while exokernel cedes resource management to library operating systems, it retains control over resource allocation and revocation, allowing for fair and efficient resource usage.

To securely multiplex the resources, an exokernel uses secure bindings. Secure bindings allow library operating systems to bind to resources while decoupling authorization from the actual use of a resource. This mechanism improves performance by expressing protection checks in terms of simple operations that can be implemented quickly and performing authorization only at bind time, which allows management to be separated from protection. Application-level software is responsible for resources with complex semantics. By isolating the need to understand these semantics to bind time, the kernel can efficiently implement access checks at access time without understanding them.

An exokernel also has to have an abort protocol. Exokernel exposes the revocation of resources as explained in the next subsection. That is, when the need arises, it must allow some time for a chosen library OS to revoke resources. When a library OS fails to respond satisfactorily to revoke requests, the kernel will have to enter a second stage of revocation protocol which the authors have named abort protocol. During this stage, the kernel breaks the secure bindings by force. The broken bindings are reported to the library OS in the form of a repossession vector, so that it can recover later.

Avoiding Management of the Resources

To protect the resources without managing them, the exokernel architecture adopts three principles in designing its interface. First, exposing allocation; the application should be allowed to ask for specific hardware resources. For example, by requesting specific memory pages an application might be able to reduce cache conflicts among its working set.

Second, exposing names; the kernel should expose the physical names to the application and remove labels. This way, the kernel eliminates the need for indirection and omits the overhead of translating virtual labels to physical names. Hiding these names from the application would hide information that can potentially be used to achieve better performance.

Finally, exposing revocation; the kernel should allow the application to choose which resources to let go of. This way, the application can optimize its performance using its knowledge of its inner workings. For example, an application can choose the least important page to be evicted from the cache.

Mach: a Flexible Modular Microkernel

[3] introduces the Mach architecture. Mach is an operating system microkernel that provides, among other things, support for networks of multiprocessors, a virtual memory design, interprocess communication, and a built-in kernel debugger.

The key advantages of the Mach kernel are flexibility, extensibility, and modularity. To achieve these properties, Mach provides only a minimal set of primitive functions. The remaining operations are performed by object-oriented user-space services. These servers determine the personality of the operating system. They can be used to achieve varying system configurations, allowing for the development of specialized applications.

Mach kernel introduces four basic abstractions: tasks, threads, ports, and messages. Tasks are the equivalent of UNIX, threads are the smallest unit of CPU utilization in tasks, ports serve as communication channels between tasks or threads, and messages are the objects sent through the ports.

Crucially, operations on objects are carried out by communicating with the designated port. For example, creating a new task or thread would return access rights to the port representing the new object. That is, this port will be used to manipulate and utilize the object. This is the heart of the design which creates both the modularity and the extensibility of the kernel.

Mach's communication mechanism is designed to work in multiprocessor environments and it draws heavily on the previous Accent network operating system developed by CMU. It uses a port mechanism for communication, allowing object-style access to resources and capability-based protection, while also supporting network transparency. The communication protocols are also used to support parallelism and virtual memory.

We shall now explore some of the features provided by Mach.

Parallelism

Mach achieves three forms of parallelism: through multiple threads communicating in a shared address space; through multiple tasks which share some section of the memory; and through multiple tasks that communicate by means of ports and messages.

Mach provides better support for parallelism compared to UNIX. It allows multiple threads within a task, as opposed to the traditional UNIX process model which only supports one thread of control per process. In Mach, a task is a high overhead object that includes a virtual address space and a set of port rights. Whereas a thread is a relatively low overhead object that specifies an execution state within a task. That is, creating new threads has a lower cost than creating new tasks Therefore, threads can be used to achieve parallelism with much lower overhead.

Virtual Memory

Mach employs a virtual memory mechanism using "memory objects." They represent the backing store for virtual memory pages. Mach allows allocating or deallocating sections of virtual memory, as well as setting the protection and inheritance (shared, copy, or none) of the pages. It also allows, for both copy-on-write and read/write sharing of memory between tasks.

In implementing the virtual memory, Mach separates the machine-dependent and machine-independent sections. The machine-independent part has full knowledge of all the virtual memory-related information. Whereas, the machine-dependent part handles low-level hardware-specific details without outside knowledge.

A key feature of Mach is that allows paging to be handled outside the kernel, as well as providing basic paging services within the kernel. When virtual memory is created, special tasks can be created to handle page faults and page-out requests, e.g. in order to implement a memory mapped file. As for the kernel services, Mach's default pager utilizes normal file systems, eliminating the need for separate paging partitions.

Kernel Debugger

Mach also supplies the developers with a built-in kernel debugger (kdb) which supports all the adb commands. It provides many debugging tools such as breakpoints, steps, stack tracing, and symbol table translation.

Mach kernel debugger also has some functionalities not available in adb. Such as enhanced stack traces, call/return trace support, and instruction counting.

Shortcomings and Limitations

The approaches given by the papers are not without their limitations and disadvantages. In this section, we discuss some of the shortcomings that relate to either one or all of these approaches. These include: issues related to highly modular systems; exokernel's dependence on the hardware; the need for OS refactorization in the library OS approach; and the limitations of independent development of OS components.

Modularity, Taken to Its Limits

While modularity allows for a clean logic and easier extensibility, it has a price. First, modularity comes with the cost of communication between the modules. Therefore, it can have an adverse effect on performance. Having more modularity means requests have to go through more layers before they are granted. That is, system calls would have a higher overhead.

Second, dependency management can be a major challenge in modular operating systems. In such systems, as pointed out by [1], modules are developed independently. As a result, it can be difficult to ensure that all dependencies are met and that the modules can communicate effectively with each other. This can lead to compatibility issues and make it challenging to maintain and update the system as a whole. It can get worse as the system becomes more and more complex and the number of modules grows.

Finally, modularity comes with security risks. Vulnerabilities in one module can potentially affect other modules and in turn corrupt the whole system. As a result, the design of modular systems should be deliberate in its attention to security, in terms of coordination between the modules. This suggests that designing modular systems may not be as easy as it seems.

Exokernel's Dependence on the Hardware

The exokernel architecture arguably relies too heavily on the hardware. This means that any changes in the hardware can have a significant impact on the exokernel's structure. For example, if a new processor architecture is introduced, the exokernel would need to be modified to support it. This sort of dependence puts the exokernel at a disadvantage where it cannot be used on a production-level scale.

Furthermore, exokernel's efficiency is achieved when applications are specially designed to take advantage of the direct access to hardware resources. This makes it challenging to run legacy applications that are designed for older hardware or applications that are not designed to work with an exokernel.

In summary, the exokernel's minimal design puts a lot on the developer's shoulders. While it has the potential to provide high performance and flexibility, its heavy reliance on hardware can limit its usefulness in practical applications. This is especially true when hardware changes occur frequently or when legacy applications need to be supported.

The Need for OS Refactorization

In [1], the authors promote replacing virtual machines with library OS versions of existing operating systems. One significant weakness of this approach is the need for refactorization. The refactorization must restructure the operating system, so that it can be broken into a kernel and library OS, all without changing its behavior. This way, the applications would continue to work on the refactorized version, and one can run multiple instances of the library OS on a single kernel to achieve isolation.

Refactorization can be a time-consuming and error-prone process, and it requires a deep understanding of the underlying operating system. Furthermore, as new components are added or removed, the system architecture may need to be modified, which can be a complex and challenging task.

Another challenge with refactorization in the context of library OS is the potential for versioning issues. The developers need to refactor not only the current versions of the commonly used operating systems, but also all the older versions. Otherwise, the library OS approach would not be able to support legacy software the same way virtual machines do. This also adds to the great cost of refactorization.

Overall, while the library OS approach offers many potential benefits, the need for refactorization is a significant weakness that must be addressed. The community must carefully consider the tradeoff between the benefits of the library OS approach and the cost of refactorization. It must also streamline the process of refactoring operating systems, so that it can be done efficiently and without error.

Independent Evolving of the Components

The library OS design does provide strong encapsulation of the host OS from the library OS, which as promised by [1] can enable independent evolution of each. But there are still potential challenges to achieving rapid evolution even in a library OS design.

Firstly, rapid evolution may be impeded by the need to maintain compatibility with the host OS. The library OS must continue to work seamlessly with the host OS, and any changes to the host OS may require corresponding changes to the interface and in turn to the library OS. This may slow down the pace of development for the library OS.

Secondly, rapid evolution may also be limited by the need to maintain compatibility with existing applications. Applications that were built to run on the monolithic kernel may not work properly on the new library OS without modifications. Therefore, the library OS must continue to support the existing application programming interfaces (APIs) while introducing new ones to enable new functionality. This balancing act may slow down the pace of evolution for the library OS.

Finally, even if the library OS can evolve rapidly on its own, its adoption may be slow if it is not compatible with existing hardware or software ecosystems. This can limit its potential impact and usage, and further slow down the pace of evolution for the library OS.

Conclusion

In conclusion, the three papers surveyed here provide valuable insights into operating system design and virtualization. The authors of these papers advocate for operating system architectures that can be customized to fit the specific needs of the application. They achieve this by creating a minimal kernel, and separating it from the operating system personality which is contained either in the library OS or in a set of user-level services.

The discussed approaches are not without limitations and challenges. The extreme modularity promoted by the papers may lead to more complexity and maintenance issues. Furthermore, the use of custom operating system libraries may require significant effort and resources to develop and maintain. Therefore, there can be little hope for these approaches to be useful in a practical setting.

There is, however, a lot of merit to the ideas at the heart of these approaches and they can be used to improve existing operating systems or design new ones. Take, for example, the unikernel architecture. It runs multiple self-sufficient units, called unikernels, on top of a bare metal hypervisor. As a result, it benefits from the nice virtualization present in traditional virtual machines while also benefiting from minimal customized kernels. Other examples include Microsoft Azure and WSL which borrow the concept of picoprocesses from Drawbirdge.