Traditionally, embedded devices have been centered around specialized, embedded processors and the embedded operating systems running on them (e.g., FreeRTOS and Zephyr). This model has been, and still is, extremely effective in ensuring efficient resource consumption, especially power, but forces developers to port applications to such OSes, since, for the most part, they are not POSIX-compliant.
Increasingly, many embedded devices are being designed around general-purpose processors, especially ARM-based ones. This is a radical shift in the way we think about embedded devices: many so-called “embedded” devices (e.g., in the IoT, edge, gateways and automotive domains) use Linux as default because it is easy to install, is POSIX-compliant and comes with a great array of applications and programming languages, not to mention a friendly, well-known development environment. The great downside is that this monolithic kernel is resource hungry: it is not unusual for a significant amount of a device’s resources to be consumed by Linux itself, leaving less for the actual application. Further, Linux is to a large extent a monolithic kernel, making it often hard or time consuming to customize (e.g., completely removing the scheduler if it is not needed, adding a new memory allocator, or trying to trim it down to reduce boot times). Finally, Linux’s significant code base (in the order of millions of lines of code) results in a large attack surface and exploits, and makes it expensive to certify in domains where safety is critical.
In order to break the dichotomy between (1) difficult-to-use but resource efficient embedded OSes and (2) power- hungry but user and application friendly general-purpose OSes such as Linux, we introduce a novel lightweight virtualization architecture and micro-library operating system called Unikraft which allows for automatically building highly specialized images for embedded devices. Unlike other approaches, Unikraft bridges the gap between resource efficiency and ease of porting with a micro-library approach, allowing for bottom-up specialization and code elimination while retaining POSIX compatibility. In addition, the extremely lean images it produces are ideal candidates for cheaper certification.
Figure 1 Unikraft’s architecture. All components are micro- libraries. Users select an architecture, a platform, the target application and Unikraft
Unikraft is fully librarized (see Figure 1): OS primitives such as the scheduler, memory allocator and even boot code are libraries. These can be removed or replaced with equivalents via a Kconfig menu as long as they comply with a set of well-defined APIs. To enable quick boot times, Unikraft can, for example, allow for the use of a simple but quick memory allocator during boot and initialization, whilst still using a different allocator for the application. Alternatively, a user could entirely remove the scheduler if not needed, and run tasks to completion in an event-driven architecture. This and many other scenarios are easy to implement in Unikraft because of its modular design.
Unikraft enables users to easily build extremely specialized, custom OSes without having to develop any actual code as it provides a POSIX-like interface which allows for running standard, off-the-shelf applications such as databases (e.g. SQLite), web servers (e.g. NGINX), key-value stores (e.g. Redis), machine learning frameworks (e.g. PyTorch and TensorFlow), and runtime language environments (e.g. Web Assembly, Python/Micropython, Lua and Ruby). In addition, Unikraft supports a number of compile-time languages including C/C++, Go, Java and Rust, with the potential for allowing different libraries to be written in different languages and combined together into a single, specialized image (i).
i S. Santhanam et al., “Towards Highly Specialized, POSIX -compliant Software Stacks with Unikraft: Work-in-Progress,” in 2020 International Conference on Embedded Software (EMSOFT), Shanghai, China, Sep. 2020, pp. 31–33, doi: 10.1109/EMSOFT51651.2020.9244044.